{
public unsafe class Device : IDisposable
{
- public float 遅延ms
+ public double 遅延sec
{
- get { return this.更新間隔ms; }
+ get { return ( this.更新間隔sec * 1000.0 ); }
}
public CSCore.CoreAudioAPI.AudioClock AudioClock
{
protected set;
} = true;
- public void 初期化する( double 希望更新間隔sec = 0.015 )
+ public void 初期化する( CSCore.CoreAudioAPI.AudioClientShareMode 共有モード, double 希望更新間隔sec = 0.015 )
{
int hr = 0;
Trace.Assert( this.Dispose済み );
this.Dispose済み = false;
+ this.共有モード = 共有モード;
+
#region " AudioClientをアクティベートする。"
//-----------------
using( var devices = new CSCore.CoreAudioAPI.MMDeviceEnumerator() )
if( 0 > hr )
System.Runtime.InteropServices.Marshal.ThrowExceptionForHR( hr );
- var 最小間隔ms = (float) 排他モードでの最小間隔100ns / 10000.0f;
-
- // 更新間隔ms を「希望更新間隔とデバイスの最小間隔の大きい方 かつ 最大1秒までの値」にする。
- this.更新間隔ms = System.Math.Min( 1000.0f, System.Math.Max( (float) ( 希望更新間隔sec * 1000.0 ), 最小間隔ms ) );
+ if( 共有モード == CSCore.CoreAudioAPI.AudioClientShareMode.Shared )
+ {
+ this.更新間隔100ns = 共有モードでの間隔100ns;
+ }
+ else
+ {
+ this.更新間隔100ns = Math.Max( FDK.Utilities.変換_sec単位から100ns単位へ( 希望更新間隔sec ), 排他モードでの最小間隔100ns );
+ }
//-----------------
#endregion
#region " デバイスフォーマットを決定する。"
//----------------
- this.WaveFormat = new CSCore.WaveFormat( 44100, 16, 2, CSCore.AudioEncoding.Pcm );
+ if( this.共有モード == CSCore.CoreAudioAPI.AudioClientShareMode.Shared )
+ {
+ this.WaveFormat = this.AudioClient.GetMixFormat();
+ }
+ else
+ {
+ this.WaveFormat = new CSCore.WaveFormat( 44100, 16, 2, CSCore.AudioEncoding.Pcm );
+ }
//----------------
#endregion
#region " AudioClient を初期化する。"
try
{
this.AudioClient.Initialize(
- CSCore.CoreAudioAPI.AudioClientShareMode.Exclusive, // 排他モード。
- CSCore.CoreAudioAPI.AudioClientStreamFlags.StreamFlagsEventCallback, // イベント駆動モード。
- (long) ( this.更新間隔ms * 10000.0f + 0.5f ), // バッファサイズ。イベント駆動モードでは、更新間隔と同じ値でなければならない。
- (long) ( this.更新間隔ms * 10000.0f + 0.5f ), // 更新間隔。
- this.WaveFormat, // バッファのフォーマット。
- Guid.Empty ); // この AudioClient = AudioStrem が所属する AudioSession。null ならデフォルトのAudioSessionに登録される。
+ this.共有モード,
+ CSCore.CoreAudioAPI.AudioClientStreamFlags.StreamFlagsEventCallback,
+ // | CSCore.CoreAudioAPI.AudioClientStreamFlags.StreamFlagsNoPersist, // 音量とミュートを記憶しない → 無効。してください
+ this.更新間隔100ns,
+ this.更新間隔100ns,
+ this.WaveFormat,
+ Guid.Empty ); // この AudioClient (= AudioStrem) が所属する AudioSession。null ならデフォルトのAudioSessionに登録される。
}
catch( CSCore.CoreAudioAPI.CoreAudioAPIException e )
{
- // 排他&イベント駆動モードの場合、バッファのアライメントエラーが返される場合がある。この場合、サイズを調整してオーディオストリームを作成し直す。
if( AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED == e.ErrorCode )
{
- int 更新間隔に一番近くてアライメントされているサイズsample = this.AudioClient.GetBufferSize();
- this.更新間隔ms = ( 更新間隔に一番近くてアライメントされているサイズsample * 1000.0f / (float) this.WaveFormat.SampleRate );
+ #region " 排他&イベント駆動モードの場合、バッファサイズアライメントエラーが返される場合がある。この場合、サイズを調整してオーディオストリームを作成し直す。"
+ //----------------
+ this.更新間隔100ns = FDK.Utilities.変換_sec単位から100ns単位へ(
+ (double) this.AudioClient.GetBufferSize() / (double) this.WaveFormat.SampleRate ); // GetBufferSize は、更新間隔に一番近い、アライメントされたバッファサイズ(sample単位)を返す。
// AudioClient を一度解放し、もう一度アクティベートし直す。
this.AudioClient.Dispose();
this.AudioClient = CSCore.CoreAudioAPI.AudioClient.FromMMDevice( 既定のデバイス );
}
- // アライメントされたサイズを使って、AudioClient を再初期化する。
+ // アライメントされたサイズを使って、AudioClient を再初期化する。それでもエラーなら例外発出。
this.AudioClient.Initialize(
- CSCore.CoreAudioAPI.AudioClientShareMode.Exclusive, // 排他モード。
- CSCore.CoreAudioAPI.AudioClientStreamFlags.StreamFlagsEventCallback, // イベント駆動モード。
- (long) ( this.更新間隔ms * 10000.0f + 0.5f ), // バッファサイズ。イベント駆動モードでは、更新間隔と同じ値でなければならない。
- (long) ( this.更新間隔ms * 10000.0f + 0.5f ), // 更新間隔。
- this.WaveFormat, // バッファのフォーマット。
- Guid.Empty ); // この AudioClient = AudioStrem が所属する AudioSession。NULLならデフォルトのAudioSessionに登録される。
-
- // それでもエラーなら例外発生。
+ this.共有モード,
+ CSCore.CoreAudioAPI.AudioClientStreamFlags.StreamFlagsEventCallback,
+ // | CSCore.CoreAudioAPI.AudioClientStreamFlags.StreamFlagsNoPersist, // 音量とミュートを記憶しない → 無効。してください
+ this.更新間隔100ns,
+ this.更新間隔100ns,
+ this.WaveFormat,
+ Guid.Empty );
+ //----------------
+ #endregion
}
}
- // 更新間隔を sample, byte 単位で保存する。
- this.更新間隔sample = this.AudioClient.GetBufferSize(); // バッファの長さはサンプル単位で返される。
- this.更新間隔byte = this.更新間隔sample * ( this.WaveFormat.Channels * this.WaveFormat.BytesPerSample );
+ this.更新間隔sample = this.AudioClient.GetBufferSize();
//-----------------
#endregion
#region " AudioRenderClient を取得する。"
#region " 情報表示。"
//-----------------
FDK.Log.Info( $"WASAPIクライアントを初期化しました。" );
- FDK.Log.Info( $" モード: 排他&イベント駆動" );
+ FDK.Log.Info( ( 共有モード == CSCore.CoreAudioAPI.AudioClientShareMode.Shared ) ?
+ " モード: 共有 & イベント駆動" :
+ " モード: 排他 & イベント駆動" );
FDK.Log.Info( $" フォーマット: {this.WaveFormat.BitsPerSample} bits, {this.WaveFormat.SampleRate} Hz" );
FDK.Log.Info( $" エンドポイントバッファ: {( (float) this.更新間隔sample / (double) this.WaveFormat.SampleRate ) * 1000.0f} ミリ秒 ({this.更新間隔sample} samples) × 2枚" );
FDK.Log.Info( $" 希望更新間隔: {希望更新間隔sec * 1000.0} ミリ秒" );
- FDK.Log.Info( $" 更新間隔: {this.更新間隔ms} ミリ秒 ({this.更新間隔sample} samples)" );
- FDK.Log.Info( $" 最小間隔: {最小間隔ms} ミリ秒" );
+ FDK.Log.Info( $" 更新間隔: {this.更新間隔sec * 1000.0} ミリ秒 ({this.更新間隔sample} samples)" );
+ if( 共有モード == CSCore.CoreAudioAPI.AudioClientShareMode.Exclusive )
+ FDK.Log.Info( $" 最小間隔: {FDK.Utilities.変換_100ns単位からsec単位へ( 排他モードでの最小間隔100ns )} ミリ秒" );
//-----------------
#endregion
// MediaFoundation が管理する、プロセス&MMCSSタスクごとに1つずつ作ることができる特別な共有ワークキューを取得、または生成して取得する。
int dwTaskId = 0;
SharpDX.MediaFoundation.MediaFactory.LockSharedWorkQueue(
- ( 11.0 > this.更新間隔ms ) ? "Pro Audio" : "Games", 0, ref dwTaskId, out this.QueueID );
+ ( 0.011 > this.更新間隔sec ) ? "Pro Audio" : "Games", 0, ref dwTaskId, out this.QueueID );
// エンドポイントバッファからの出力要請イベントを作成し、AudioClient に登録する。
this.出力要請イベント = CreateEvent( IntPtr.Zero, false, false, "WASAPI出力要請イベント" );
this.Mixer.サウンドを削除する( sound );
}
- // WASAPI オブジェクト
+ protected CSCore.CoreAudioAPI.AudioClientShareMode 共有モード;
protected CSCore.CoreAudioAPI.AudioClient AudioClient = null;
protected CSCore.CoreAudioAPI.AudioRenderClient AudioRenderClient = null;
-
- // エンドポイントバッファ情報
- protected int 更新間隔sample = 0; // デバイスから取得する。
- protected float 更新間隔ms;
- protected int 更新間隔byte;
protected CSCore.WaveFormat WaveFormat = null;
+ protected long 更新間隔100ns = 0;
+ protected int 更新間隔sample = 0;
+ protected int 更新間隔byte => ( this.更新間隔sample * this.WaveFormat.Channels * this.WaveFormat.BytesPerSample );
+ protected double 更新間隔sec => ( FDK.Utilities.変換_100ns単位からsec単位へ( this.更新間隔100ns ) );
// ミキサー。サウンドリストもここ。
protected Mixer Mixer = null;
// ・このサービスインターフェースは、シングルスレッド(GUIスレッド)で同期実行される。(StrokeStyleT クラスの ServiceBehavior属性を参照。)
// ・このサービスホストはシングルトンであり、すべてのクライアントセッションは同一(単一)のサービスインスタンスへ接続される。(Program.Main() を参照。)
+ /// <summary>
+ /// 曲を読み込み、演奏を開始する。
+ /// </summary>
+ /// <remarks>
+ /// ビュアーモードのときのみ有効。
+ /// </remarks>
+ /// <param name="path">曲ファイルパス</param>
+ /// <param name="startPart">演奏開始小節番号(0..)</param>
+ /// <param name="drumsSound">ドラムチップ音を発声させるなら true。</param>
public void ViewerPlay( string path, int startPart = 0, bool drumsSound = true )
{
if( StrokeStyleT.ビュアーモードではない )
ドラムチップ発声 = drumsSound,
} );
}
+ /// <summary>
+ /// 現在の演奏を停止する。
+ /// </summary>
+ /// <remarks>
+ /// ビュアーモードのときのみ有効。
+ /// </remarks>
public void ViewerStop()
{
if( StrokeStyleT.ビュアーモードではない )
種別 = ViewerMessageType.演奏停止,
} );
}
+ /// <summary>
+ /// サウンドデバイスの発声遅延[ms]を返す。
+ /// </summary>
+ /// <returns>遅延量[ms]</returns>
public float GetSoundDelay()
{
if( StrokeStyleT.ビュアーモードではない )
return 0f;
- return ( null != StrokeStyleT.Wasapiデバイス ) ? StrokeStyleT.Wasapiデバイス.遅延ms : 0f;
+ return ( null != StrokeStyleT.Wasapiデバイス ) ? (float) ( StrokeStyleT.Wasapiデバイス.遅延sec * 1000.0 ) : 0f;
}
//----------------
#endregion
#region " WASAPI デバイスを初期化する。"
//----------------
StrokeStyleT.bs_Wasapiデバイス = new FDK.メディア.サウンド.WASAPI.Device();
- StrokeStyleT.bs_Wasapiデバイス.初期化する( 15.0f );
+ StrokeStyleT.bs_Wasapiデバイス.初期化する( CSCore.CoreAudioAPI.AudioClientShareMode.Exclusive, 0.015 );
//----------------
#endregion
#region " キーボード入力 を初期化する。"