1
0
mirror of synced 2024-12-01 10:37:22 +01:00
OpenTaiko/FDK19/コード/03.サウンド/CSoundDeviceDirectSound.cs
2021-09-21 00:16:38 +02:00

279 lines
9.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading;
using SlimDX;
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;
}
}