1
0
mirror of synced 2024-12-24 13:24:59 +01:00
OpenTaiko/FDK19/コード/03.サウンド/CSoundDeviceDirectSound.cs

278 lines
9.5 KiB
C#
Raw Normal View History

2021-09-21 00:16:38 +02:00
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading;
using SharpDX.DirectSound;
namespace FDK
{
internal class CSoundDeviceDirectSound : ISoundDevice
{
// プロパティ
public ESoundDeviceType e出力デバイス
{
get;
protected set;
}
public long n実出力遅延ms
{
get;
protected set;
}
public long n実バッファサイズms
{
get;
protected set;
}
public static readonly BufferFlags DefaultFlags = BufferFlags.Defer | BufferFlags.GetCurrentPosition2 | BufferFlags.GlobalFocus | BufferFlags.ControlVolume | BufferFlags.ControlPan | BufferFlags.ControlFrequency;
// CSoundTimer 用に公開しているプロパティ
public long n経過時間ms
{
get
{
if ( ctimer != null )
{
this.sd経過時間計測用サウンドバッファ.DirectSoundBuffer.GetCurrentPosition(out int n現在位置, out int _);
long n現在のシステム時刻ms = this.tmシステムタイマ.nシステム時刻ms;
// ループ回数を調整。
long nシステム時刻での間隔ms = n現在のシステム時刻ms - this.n前に経過時間を測定したシステム時刻ms;
while ( nシステム時刻での間隔ms >= n単位繰り上げ間隔ms ) // 前回から単位繰り上げ間隔以上経過してるなら確実にループしている。誤差は大きくないだろうから無視。
{
this.nループ回数++;
nシステム時刻での間隔ms -= n単位繰り上げ間隔ms;
}
if ( n現在位置 < this.n前回の位置 ) // 単位繰り上げ間隔以内であっても、現在位置が前回より手前にあるなら1回ループしている。
this.nループ回数++;
// 経過時間を算出。
long n経過時間ms = (long) ( ( this.nループ回数 * n単位繰り上げ間隔ms ) + ( n現在位置 * 1000.0 / ( 44100.0 * 2 * 2 ) ) );
// 今回の値を次回に向けて保存。
this.n前に経過時間を測定したシステム時刻ms = n現在のシステム時刻ms;
this.n前回の位置 = n現在位置;
return n経過時間ms;
}
else
{
long nRet = ctimer.nシステム時刻ms - this.n前に経過時間を測定したシステム時刻ms;
if ( nRet < 0 ) // カウンタがループしたときは
{
nRet = ( ctimer.nシステム時刻 - long.MinValue ) + ( long.MaxValue - this.n前に経過時間を測定したシステム時刻ms ) + 1;
}
this.n前に経過時間を測定したシステム時刻ms = ctimer.nシステム時刻ms;
return nRet;
}
}
}
public long n経過時間を更新したシステム時刻ms
{
get { throw new NotImplementedException(); }
}
public CTimer tmシステムタイマ
{
get;
protected set;
}
public int nMasterVolume
{
get
{
return (int) 100;
}
set
{
// 特に何もしない
}
}
// メソッド
public CSoundDeviceDirectSound( IntPtr hWnd, long n遅延時間ms, bool bUseOSTimer )
{
Trace.TraceInformation( "DirectSound の初期化を開始します。" );
this.e出力デバイス = ESoundDeviceType.Unknown;
this.n実バッファサイズms = this.n実出力遅延ms = n遅延時間ms;
this.tmシステムタイマ = new CTimer( CTimer.E種別.MultiMedia );
#region [ DirectSound ]
//-----------------
this.DirectSound = new DirectSound(); // 失敗したら例外をそのまま発出。
// デバイスの協調レベルを設定する。
bool priority = true;
try
{
this.DirectSound.SetCooperativeLevel( hWnd, CooperativeLevel.Priority );
}
catch
{
this.DirectSound.SetCooperativeLevel( hWnd, CooperativeLevel.Normal ); // これでも失敗したら例外をそのまま発出。
priority = false;
}
// デバイス作成完了。
this.e出力デバイス = ESoundDeviceType.DirectSound;
//-----------------
#endregion
if ( !bUseOSTimer )
{
#region [ ]
//-----------------
// 単位繰り上げ間隔[秒]の長さを持つ無音のサウンドを作成。
uint nデータサイズbyte = n単位繰り上げ間隔sec * 44100 * 2 * 2;
var ms = new MemoryStream();
var bw = new BinaryWriter( ms );
bw.Write( (uint) 0x46464952 ); // 'RIFF'
bw.Write( (uint) ( 44 + nデータサイズbyte - 8 ) ); // ファイルサイズ - 8
bw.Write( (uint) 0x45564157 ); // 'WAVE'
bw.Write( (uint) 0x20746d66 ); // 'fmt '
bw.Write( (uint) 16 ); // バイト数
bw.Write( (ushort) 1 ); // フォーマットID(リニアPCM)
bw.Write( (ushort) 2 ); // チャンネル数
bw.Write( (uint) 44100 ); // サンプリング周波数
bw.Write( (uint) ( 44100 * 2 * 2 ) ); // bytes/sec
bw.Write( (ushort) ( 2 * 2 ) ); // blockサイズ
bw.Write( (ushort) 16 ); // bit/sample
bw.Write( (uint) 0x61746164 ); // 'data'
bw.Write( (uint) nデータサイズbyte ); // データ長
for ( int i = 0; i < nデータサイズbyte / sizeof( long ); i++ ) // PCMデータ
bw.Write( (long) 0 );
var byArrWaveFleImage = ms.ToArray();
bw.Close();
ms = null;
bw = null;
this.sd経過時間計測用サウンドバッファ = this.tサウンドを作成する( byArrWaveFleImage, ESoundGroup.Unknown );
CSound.listインスタンス.Remove( this.sd経過時間計測用サウンドバッファ ); // 特殊用途なのでインスタンスリストからは除外する。
// サウンドのループ再生開始。
this.nループ回数 = 0;
this.n前回の位置 = 0;
this.sd経過時間計測用サウンドバッファ.DirectSoundBuffer.Play( 0, PlayFlags.Looping );
this.n前に経過時間を測定したシステム時刻ms = this.tmシステムタイマ.nシステム時刻ms;
//-----------------
#endregion
}
else
{
ctimer = new CTimer( CTimer.E種別.MultiMedia );
}
Trace.TraceInformation( "DirectSound を初期化しました。({0})({1})", ( priority ) ? "Priority" : "Normal", bUseOSTimer? "OStimer" : "FDKtimer" );
}
public CSound tサウンドを作成する( string strファイル名, ESoundGroup soundGroup )
{
var sound = new CSound(soundGroup);
sound.tDirectSoundサウンドを作成する( strファイル名, this.DirectSound );
return sound;
}
private CSound tサウンドを作成する( byte[] byArrWAVファイルイメージ, ESoundGroup soundGroup )
{
var sound = new CSound(soundGroup);
sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound );
return sound;
}
// 既存のインスタンス(生成直後 or Dispose済みに対してサウンドを生成する。
public void tサウンドを作成する( string strファイル名, CSound sound )
{
sound.tDirectSoundサウンドを作成する( strファイル名, this.DirectSound );
}
public void tサウンドを作成する( byte[] byArrWAVファイルイメージ, CSound sound )
{
sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound );
}
public void tサウンドを作成する( byte[] byArrWAVファイルイメージ, BufferFlags flags, CSound sound )
{
sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound, flags );
}
#region [ Dispose-Finallizeパターン実装 ]
//-----------------
public void Dispose()
{
this.Dispose( true );
GC.SuppressFinalize( this );
}
protected void Dispose( bool bManagedDispose )
{
this.e出力デバイス = ESoundDeviceType.Unknown; // まず出力停止する(Dispose中にクラス内にアクセスされることを防ぐ)
if ( bManagedDispose )
{
#region [ ]
//-----------------
if ( this.sd経過時間計測用サウンドバッファ != null )
{
this.sd経過時間計測用サウンドバッファ.tサウンドを停止する();
C共通.tDisposeする( ref this.sd経過時間計測用サウンドバッファ );
}
//-----------------
#endregion
#region [ ]
//-----------------
if( this.th経過時間測定用スレッド != null )
{
this.th経過時間測定用スレッド.Abort();
this.th経過時間測定用スレッド = null;
}
//-----------------
#endregion
C共通.tDisposeする( ref this.DirectSound );
C共通.tDisposeする( this.tmシステムタイマ );
}
if ( ctimer != null )
{
C共通.tDisposeする( ref this.ctimer );
}
}
~CSoundDeviceDirectSound()
{
this.Dispose( false );
}
//-----------------
#endregion
protected DirectSound DirectSound = null;
protected CSound sd経過時間計測用サウンドバッファ = null;
protected Thread th経過時間測定用スレッド = null;
// protected AutoResetEvent autoResetEvent = new AutoResetEvent( false );
protected const uint n単位繰り上げ間隔sec = 1; // [秒]
protected const uint n単位繰り上げ間隔ms = n単位繰り上げ間隔sec * 1000; // [ミリ秒]
protected int nループ回数 = 0;
private long n前に経過時間を測定したシステム時刻ms = CTimer.n未使用;
private int n前回の位置 = 0;
private CTimer ctimer = null;
}
}