Default Streaming Manager Information 低レベルI/Oは、高レベル Stream Manager APIのデフォルト実装サブモジュールで、高レベル Stream Manager API より実装がはるかにシンプルな I/O転送用インターフェースの提供を意図としています。従って、低レベルI/Oは、Stream Manager デフォルト実装を前提とするものです。 |
低レベルI/Oシステムは、Audiokinetic の Stream Manager(ストリームマネージャ)実装に固有のものです。そのインターフェースは、<Wwise installation>="">/SDK/include/AK/SoundEngine/AkStreamMgrModule.h で定義されています。
低レベルI/Oシステムには2つの目的があります:
File Location Resolver(ファイルロケーションリゾルバ)と呼ばれる AK::StreamMgr::IAkFileLocationResolver
を実装する唯一のオブジェクトを、(AK::StreamMgr::SetFileLocationResolver()
を使用して)Stream Manager に登録する必要があります。Stream Manager は、標準または自動ストリームを作成するたびに AK::StreamMgr::IAkFileLocationResolver::Open()
を呼び出します。 このメソッドは、該当ファイルをストリームするために使用する必要がある高レベルストリーミングデバイスのIDを、ファイルサイズやその他の情報とともに含んでいる有効なファイル記述子(AkFileDesc
)を返す必要があります。 ストリーミングデバイスは、AK::StreamMgr::CreateDevice()
を使用して Stream Manager にて作成および登録されます。
各ストリーミングデバイスには、低レベルI/Oフックが提供される必要があります。低レベルI/Oフックは次の2つのインターフェースのいずれかを実装します:AKStreamMgr::IAkIOHookBlocking または AK::StreamMgr::IAkIOHookDeferred。ストリーミングデバイスはI/O転送を実行する必要がある場合には、低レベルI/Oフックの
Read()
または Write()
メソッドを使用します。 高レベル Stream Manager からは、プラットフォームI/O API への直接呼出しは発行されません。File Location Resolver とI/Oフックは、ともに低レベルI/Oシステムを構成します。ストレージデバイスの制約、ファイルおよびプラットフォームI/Oに固有のものはすべて低レベルI/Oシステムによって処理されます。
次の図は、低レベルI/Oシステムのインターフェースと、それらがデフォルト Stream Manager にどのように表示されるかを示したものです。
ゲームタイトルは、ファイル位置を解決し実際のI/O転送を実行するために、低レベルI/Oインターフェースを実装する必要があります。Wwise サウンドエンジンのI/O管理をゲームに統合する最も簡単で効率の良い方法は、Stream Manager のデフォルト実装を使用し、低レベルI/Oシステムを実装することです。 そこから、ネイティブファイル読み込みの実行や、I/Oリクエストの独自I/O管理技術へのルーティングなどを行うことができます。
WwiseのSDKには、そのまま使用可能な低レベルI/O用のデフォルト実装が含まれています。また、これを独自の実装への出発点として使用することもできます。低レベルI/Oサンプルの詳細な概要については、サンプル デフォルト実装チュートリアル を参照してください。
AK::StreamMgr::SetFileLocationResolver() を使用して、File Location Resolver(ファイルロケーションリゾルバ)を登録する必要があります。 Stream Manager がストリームオブジェクトを作成するたびに、リゾルバがこのメソッドの Open() メソッドを呼び出します。 AK::StreamMgr::IAkFileLocationResolver::Open()
は、ファイル記述子構造体(AkFileDesc
)のフィールドを入力する必要があります。 Stream Manager 内にあるストリームオブジェクトと同数のファイルが同時に低レベル I/Oに存在します。
ゲームは、AK::StreamMgr::CreateDevice()
を使用して、少なくとも1つのストリーミングデバイスを Stream Manager に作成する必要がありますが。 必要に応じていくらでもデバイスを作成することができます。各ストリーミングデバイスは、独自スレッドで実行され、I/O要求を独自I/Oフックに送信します。一般的に、物理デバイス1つにつきストリーミングデバイスを1つ作成する必要があります。 AkFileDesc 構造体には、deviceID と呼ばれるフィールドがあります。File Location Resolverは、これを作成されたストリーミングデバイスのいずれかの deviceID に設定しなければなりません。このようにして、ファイルハンドリングが適切なデバイスにディスパッチされます。さらに、ファイルサイズとオフセット (uSector
) が返され、システムファイルハンドルが作成される必要があります(これは必須ではありません)。
Stream Manager のクライアントは、文字列 (const AkOSChar *)、ID (AkFileID, integer) またはファイル記述子を使用してファイルを識別する必要があります。Stream Manager のストリーム作成メソッド (AK::IAkStreamMgr::CreateStd()、AKIAkStreamMgr::CreateAuto()
) にオーバーロードが2つあるのはこのためです。File Location Resolver は、ファイル名とファイルIDをファイル記述子にマッピングするタスクを持ちます、従って、AKStreamMgr::IAkFileLocationResolver::Open() のオーバーロードが2つになります。ファイル記述子は、すべての低レベル転送と情報クエリのためのデータソースを識別するために使用されます。
AK::StreamMgr::IAkFileLocationResolver::Open()
は、ファイル記述子を充填する必要があります。このファイル記述子は、低レベルI/Oフックへの呼び出しのたびに戻されます。構造体の3つのメンバが高レベル Stream Manager によって使用されます:
AkFileDesc::hFile
で表されるファイルの先頭に対して相対的。このファイル記述子に対して AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize()
によって返される値に対応するサイズを持つブロック(セクタ)によって表されます。 ストリーミングデバイスは、I/O転送のためのメソッドを呼び出す時に、ファイルの先頭からのオフセット(バイト単位)を AkTransferInfo 構造体の一部として低レベルI/Oフックに送信します。オフセットは、次のように計算されます:Current_Position + ( AkFileDesc::uSector * Block_Size)。ブロックサイズも、各ファイル記述子ごとに1回、このフックを介してクエリされることに注意してください。AK::IAkStdStream::GetPosition()
, AK::IAkAutoStream::GetPosition()
を介して)ユーザーにレポートし、自動ストリームのI/O転送を停止します。残りのメンバは、低レベルI/Oシステムが排他的に所有します。例えば、Win32 で HANDLE にタイプ定義されている AkFileHandle
は、Win32 の ReadFile()
に渡された実有効ファイルハンドルを保持するために使用することができます。一方、これをIDまたはポインタとして使用することも可能です。高レベルデバイスがファイル記述子フィールドを変更または読み取ることはありません。そのため、ファイルがゲームディスク上に重複してレイアウトされている場合などに、低レベルI/Oはファイルハンドルを自由に閉じて開きなおすことができます。
|
Tip: AK::StreamMgr::IAkFileLocationResolver::Open() に渡されるファイル記述子構造 in_fileDesc のアドレスは、これに関連するストリームオブジェクトの存続期間を通じて変わりません。 |
AK::StreamMgr::IAkFileLocationResolver::Open()
は、別の引数 io_bSyncOpen を受け入れます。io_bSyncOpen が true の場合、ファイルは直ちに開かれなければならず、その記述子に有効な情報が含まれている必要があることを意味します。一方、io_bSyncOpen が false の場合、File Location Resolver はファイルオープンの待機を決定することができます。なぜ待機するのでしょうか? AK::IAkStreamMgr::CreateStd() または AK::IAkStreamMgr::CreateAuto()
が、AKStreamMgr::IAkFileLocationResolver::Open() をクライアントのスレッドに呼び出すからです。 ファイルのオープンにかなりの時間がかかる場合は、I/Oスレッドまでオープンを遅延(defer)させるのが得策かもしれません。例えば、サウンドエンジンはオーディオスレッドにストリームを作成しますが、これは、Open()
でブロックされます。ファイルオープンに時間がかかりすぎると、音声が途切れてしまうことがあります(音声の枯渇)。
io_bSyncOpen
が false の場合、ファイルを即座にオープンすることも即座にオープンしないことも可能です。前者の場合は、オープンしたことを示すために、 AkFileDesc
構造体を埋め、フラグを true に設定する 必要があります。オープンしない場合は、AkFileDesc の deviceID フィールドを設定する以外は何も行う必要がありません。これは、どのデバイスのスレッドが該当ファイルを処理するのかを Stream Manager に知らせる必要があるため、非常に重要です。いったんファイルがデバイスにディスパッチされると、これを変更することはできません。 それ以降の deviceID への変更は無視されます。
|
Caution: 即座にオープンすることを要求されたファイルのオープン遅延はできません。つまり、io_bSyncOpen をリセットすることはできません。 |
オープンを遅延させる場合、AkFileDescdeviceID によって指定されたストリーミングデバイスは、このストリームオブジェクトの所有権を取得します。 このデバイスは、そのI/Oフックに独自スレッドから Read()
を呼び出す前に、 io_bSyncOpen
を true
に設定して、再び AK::StreamMgr::IAkFileLocationResolver::Open()
を呼び出すことになります。
|
Caution: ファイルオープン遅延には、負担があります。ファイルオープンデータ(フラグとファイル名)は、 AK::StreamMgr::IAkFileLocationResolver::Open() が再度呼び出されるまで格納されなければなりません。必要がない場合は、ファイルオープン遅延を避けてください。 |
Stream Managerの AK::IAkStreamMgr::CreateStd()
と AK::IAkStreamMgr::CreateAuto()
メソッドは、AkFileSystemFlags structure 構造体へのポインタを受け取り、これは AK::StreamMgr::IAkFileLocationResolver::Open()
へ渡されます。この構造は、ユーザーから低レベルI/Oへ直接情報を渡すための方法です。この情報は、例えばファイルロケーションのロジックを完了するために使用されています。Stream Manager の汎用ユーザーは NULL を渡すことができますが、File Location Resolver がサウンドエンジンから要求が来ることを認識できるよう、サウンドエンジンは常に関連情報が入った構造体を渡します。
ファイルシステムのフラグ構造体には以下のフィールドが含まれます:
サウンドエンジンは、SoundBank ファイルとストリームオーディオファイルを読み込みます。このサブセクションでは、低レベルI/O がその識別子を実際のファイルに解決する方法について説明します。
AK::StreamMgr::IAkFileLocationResolver::Open() で受け取られたIDを低レベルI/O の有効なファイル記述子にマップするための戦略として、次のいずれかが可能です:
低レベルI/O の SDK サンプルは、2つの異なる戦略を使用してファイルロケーションを解決します。そのうちの1つ (CAkFileLocationBase) は、パスを連結して、グローバル設定を行い、プラットフォームの fopen()
メソッドで使用可能な完全なパスの文字列を作成します。もう1つ (CAkFilePackageLowLevelIO
) は、File Packager ユーティリティで作成されたファイルパッケージを管理します。ファイルパッケージは、多数のファイルが連結された大きなファイルのことで、それぞれの元のファイルの相対オフセットを示すヘッダーを持ちます。
File Location Resolvers(ファイルロケーションリゾルバ)の実装は、SDK サンプルとして提供されています。これらは、ファイルロケーションを解決するために異なる戦略を使用します。これらの戦略については、このセクションの最後のサンプルコードチュートリアルで説明されています。
低レベルI/Oのデフォルト実装に使用される戦略については、基本的なファイルロケーション を参照してください。
ファイルパッケージを使用する低レベルI/O実装 (CAkFilePackageLowLevelIO) に使用されるストラテジーの解説は、 ファイルロケーション を参照してください。
メインAPIからのバンクをロードするための明示的または暗黙的な要求 (AK::SoundEngine::LoadBank()
および AK::SoundEngine::PrepareEvent()
) に続いて、サウンドエンジンの Bank Manager は、AK::IAkStreamMgr::CreateStd()
の ANSI 文字列またはIDオーバーロードを呼び出します。選択されるオーバーロードを決定する条件についての詳細は、 ファイルシステムからのバンクのロード を参照してください。
いずれのケースにおいても、ファイルシステムフラグ内の企業ID(company ID)として AKCOMPANYID_AUDIOKINETIC 、コーデックID(codec ID)として AKCODECID_BANK が使用されます。
サウンドエンジンAPIの LoadBank()
メソッドは、バンクが言語固有であるかどうかを指定するフラグを公開しません。これの解決は、低レベルI/Oの 実装しだいです。 サウンドエンジンは SoundBank の言語固有性を認識しないため、bIsLanguageSpecific フラグを True に設定した状態で Stream Manager を呼び出します。Stream Manager(低レベルI/O)がこれのオープンに失敗すると、サウンドエンジンは bIsLanguageSpecific
フラグを False
に設定して再試行します。Wwise SDK での言語固有バンク操作に関する詳細は 言語固有の ("Voice" および "Mixed") Soundbanks を参照してください。
|
Tip: ローカライズされていないバンクのための Bank Manager による2度の Stream Manager 呼び出しを回避するには、bIsLanguageSpecific フラグを無視してサウンドバンクを正しい場所で直接開きます。サウンドバンクの内容と位置を把握しているのはユーザーのみとなります。また、AKSoundEngine::LoadBank() の非同期バージョンに渡されたクッキーは、in_pFlags->pCustomData の値として AK::StreamMgr::IAkFileLocationResolver::Open() に渡されます。バンクが言語固有ディレクトリから開かれるべきかどうかを判断するためにこれを使用することができます。 |
ストリーミングファイルのリファレンスは、整数IDとしてバンクに格納されています。ストリーミングされることになっている変換されたオーディオファイルの実ファイルパスは、バンクと一緒に生成される SoundBanksInfo.xml ファイルに入っています(詳細は、SoundBanksInfo.xml を参照してください)。
|
Tip: WwiseのSoundBank 設定で "Copy streamed files(ストリーミングファイルをコピーする)"を選択すると、スキームの [ID].[ext] に改名して特定プラットフォームの Generated SoundBanks(生成されたサウンドバンク)フォルダにコピーすることができます。 詳細については ストリーミングオーディオファイル を参照して下さい。 デフォルトファイルロケーション実装 (CAkFileLocationBase) は、"Copy streamed files" オプションで使用されることを想定して作られています。 |
サウンドエンジンはストリーミングオーディオファイルを再生しようとする時、AKIAkStreamMgr::CreateAuto() の IDオーバーロードを呼び出します。これは、AKStreamMgr::IAkFileLocationResolver::Open() のIDオーバーロードまで伝達されます。IDに加えて、次の情報を含む AkFileSystemFlags 構造体が渡されます:
いったんファイルロケーションが解決されると、Stream Manager は、独自I/Oフックを介して低レベルI/Oシステムとやりとりする適切なストリーミングデバイスにファイル記述子を渡します。AKStreamMgr::IAkLowLevelIOHook::GetBlockSize() への呼び出しにより、低レベルブロックサイズによる制約がまず最初にクエリされます。 続いて、すべての入力データ転送がフックの Read() メソッドを介して、出力データ転送がフックの Write() メソッドを介して実行されます。 ストリームが破棄されると AK::StreamMgr::IAkLowLevelIOHook::Close() が呼び出されます。
これらのすべてのメソッドには、File Location Resolver によって充填されたものと同じファイル記述子が渡されます。
現在の Stream Manager 実装は、2種類のストリーミングデバイスを定義しますが、その結果として、2種類のI/Oフックを定義します。そのうちの1つは、低レベルI/Oとの同期ハンドシェーク (AK::StreamMgr::IAkIOHookBlocking) を使用するのに対して、もう一方は非同期ハンドシェーク (AK::StreamMgr::IAkIOHookDeferred) を使用します。これらの2つのインターフェースは、AKStreamMgr::IAkLowLevelIOHook から継承されます。
AK::StreamMgr::CreateDevice() には、デバイス全体の初期化設定を含む AkDeviceSettings と呼ばれる構造体が渡されます。そのうちの1つ AkDeviceSettings::uSchedulerTypeFlags は、高レベルI/Oスケジューラタイプを指定します。AKStreamMgr::IAkIOHookBlocking で動作するブロッキング(blocking)デバイスを作成したい場合は、これを AK_SCHEDULER_BLOCKING に設定、AKStreamMgr::IAkIOHookDeferred で動作する遅延(deferred)デバイスを作成したい場合は、AK_SCHEDULER_DEFERRED_LINED_UP に設定します。また、適切な低レベルI/Oフックのインスタンスが AK::StreamMgr::IAkIOHookDeferred に渡される必要があります。
|
Caution: AK::StreamMgr::IAkIOHookDeferred のインスタンスを AK_SCHEDULER_BLOCKING デバイスに渡すこと、またはその逆は違法ですが、これを行うと自動的に作成時に失敗し、実行時にクラッシュします。 |
AK::StreamMgr::CreateDevice() は、File Location Resolver によってファイルディスクリプタ(記述子)構造体に設定されているデバイスIDを返します。
以下の2つのセクションでは、ブロッキング(blocking)I/Oフックおよび遅延(deferred )I/Oフックについて説明します。
AK_SCHEDULER_BLOCKING フラグが指定されている場合、Stream Manager は AK::StreamMgr::IAkIOHookBlocking インターフェースを介して低レベルI/Oシステムとやり取りするストリーミングデバイスを作成します。
ブロッキング(blocking)インターフェースは、遅延(deferred)インターフェースよりシンプルです。これは2つのメソッド、AKStreamMgr::IAkIOHookBlocking::Read() と AK::StreamMgr::IAkIOHookBlocking::Write() を定義しますが、これらのメソッドはI/O転送が完了した時にのみ返される必要があります。
|
Caution: 低レベルI/Oレベルの同期I/O転送と Stream Manager レベル(あるいは AK::SoundEngine::LoadBank() のサウンドエンジンレベル)の同期ストリームアクセスを混同しないでください。これらは完全に無関係です。ストリーミングデバイスは、独自のスレッドで実行されます。このスレッドは、低レベルI/O転送を、高レベル IAkStdStream と IAkAutoStream アクセスで分離させます。従って、ブロッキング(blocking ) 低レベルI/Oフックは、Stream Manager の非ブロッキング(non-blocking )ストリームアクセスと遅延(deferred )I/Oフックを処理し、またその逆の処理も行われます。 |
AK_SCHEDULER_BLOCKING デバイスは、実装の内部に書かれているアルゴリズムに従って、優先的にサービスされるべき高レベルストリームオブジェクトを選択します。続いてこのストリームオブジェクトに関連付けられているファイル記述子を使用して Read() または Write() を呼び出します。また、ファイル内の転送開始位置や要求転送サイズを指定する AkIOTransferInfo 構造体と、データの書き込みまたは読み込みのためのバッファアドレスへのポインタを渡します。さらに、この転送用のヒューリスティックを渡します。転送ヒューリスティックは、構造体 AkIoHeuristics で定義され、ミリ秒単位のデッドライン、AK_MIN_PRIORITY と AK_MAX_PRIORITY 間のプライオリティ値を含んでいます。ストリーミングデバイスのスケジューラは、デッドラインが最小で優先度の最も高い転送要求が低レベルI/Oに送信されるように作られています。
|
Note:
|
Read() または Write() の内部では、データ転送が完全に実行される必要があります。転送が正常に完了すると、この関数は AK_Success を返します。正常に完了しなかった場合は、AK_Fail を返します。
AK_SCHEDULER_BLOCKIN AK_SCHEDULER_DEFERRED_LINED_UP フラグが指定されている場合、Stream Manager は AK::StreamMgr::IAkIOHookDeferred インターフェースを介して低レベルI/Oシステムとやり取りするストリーミングデバイスを作成します。
遅延(deferred)インターフェースは、ブロッキング(blocking)インターフェースより複雑で。同時に複数の転送要求を処理する低レベルI/O実装での使用を意図されています。これは、3つのメソッド、AKStreamMgr::IAkIOHookDeferred::Read()、AKStreamMgr::IAkIOHookDeferred::Write() と AK::StreamMgr::IAkIOHookDeferred::Cancel() を定義します。Read() と Write() はすぐに関数を返し、コールバック関数を介して転送が完了したことをストリーミングデバイスに知らせます。ストリーミングデバイスが初期設定で低レベルI/Oに送信する可能性のある同時I/O転送の最大数を指定する必要があります(AkDeviceSettingsuMaxConcurrentIO)。
コールバック関数に AK_Fail(または、AK_Success 以外)を渡したり、AKStreamMgr::IAkIOHookDeferred::Read()/Write() から AK_Fail を返すと、ストリームが破棄され転送ログに "I/O error(I/O エラー)" 通知が表示されます。
|
Caution: AK::StreamMgr::IAkIOHookDeferred::Read() (または Write()) から AK_Fail を返すと、ストリーミングデバイスはコールバック関数を待たないので、これを呼び出すべきではありません。 |
|
Tip: 複数の要求を同時に処理する際に、ご使用の実装が優れたパフォーマンスを実現できる場合には AK_SCHEDULER_DEFERRED_LINED_UP デバイスは便利です。そうでない場合は、AK_SCHEDULER_BLOCKING デバイスを使用するべきです。 例えば、ReadFile をブロック(block)する代わりに、Win32 OVERLAPPED ReadFile() を使用しても利得がありません。ReadFile() は、I/O スレッドによって Stream Manager インターフェースから既に分離されていることを思い出してください。 一方、遅延デバイスにはいくつかの欠点があります。より多くのメモリを使用し、I/O転送はブロッキングデバイスの場合より早期に発行されるため、フラッシュまたはキャンセルされる可能性が高くなります。例えば、スケジューラはストリーム用の複数の転送を一度に送出する可能性があり、サウンドエンジンが SetPosition() を呼び出すと、すべての未解決の転送が完了した時点でフラッシュされます。 |
|
Note: AkDeviceSettings::uMaxConcurrentIO は、デバイスが低レベルI/Oに送出する転送要求の最大数を表します。その他の点では、AK_SCHEDULER_DEFERRED_LINED_UP はデバイススケジューラは、AK_SCHEDULER_BLOCKING スケジューラと全く同様に動作します:これは、Stream Manager のクライエントが AK::IAkStdStream::Read()/Write() を呼び出した場合、または、実行中の自動ストリームのバッファリングがバッファリングターゲット(AkDeviceSettingsfTargetAutoStmBufferLength、ターゲットバッファリング長の詳細は Audiokinetic ストリームマネージャ初期化設定 を参照)以下の場合のみ、転送要求の送出を決定します。 |
ストリーミングデバイスは、AkAsyncIOTransferInfo 構造体を Read() と Write() へ渡します。この構造体は、転送完了時に呼び出される必要のあるコールバック関数についての情報で、前述の AkIOTransferInfo 構造体を拡張します。また、この構造体には、バッファアドレスが含まれており、これが Read() または Write() へ別々に渡されることはありません。実装者による保留中I/O転送へのメタデータのアタッチを支援するため、pUserData フィールドも提供されています。AkAsyncIOTransferInfo 構造体は、コールバックが呼び出されるまで生きています。コールバック呼出し後は、これを参照しないようにしてください。
AkIoHeuristics 構造体も Read() または Write() に渡されます。これに含まれている情報は、I/O要求シャッフルのために独自のI/Oストリーミング技術へ Read() または Write() 呼び出しをルーティングする場合に有益になる可能性があります。
|
Tip: デフォルト Stream Manager のスケジューラ実装は、「ディスク帯域幅ヒューリスティック」ではなく「クライアントヒューリスティック」に基づいています。Stream Manager は、ディスク上ファイルのレイアウトを認識しません。独自のストリーミング技術でレイアウト認識が可能な場合は、これを使ってI/O要求を並べ替えてディスクのシークを最小限にすることができます。 |
ストリーミングデバイスは、時折データをフラッシュする必要があります。これは、Stream Manager のクライアントが AK::IAkAutoStream::SetPosition() を呼び出した時、または、ルーピングヒューリスティックを変更した場合に発生する可能性があります。時によっては、対応する転送が完了する前にデータがフラッシュされる必要がある場合があります。これは、AKIAkAutoStream::SetPosition() と AkDeviceSettings::fTargetAutoStmBufferLength が大きい場合に発生する可能性が高くなります。遅延I/OフックAPI は、これが発生した場合に通知を受けるためのエントリポイントを提供します:Cancel()。ストリーミングデバイスは、低レベルI/Oで保留中のI/O転送に関連するデータをフラッシュする必要がある場合に、この転送を "cancelled" と内部でタグ付けし、AKStreamMgr::IAkIOHookDeferred::Cancel() を呼び出し、コールバックの呼び出しを待ちます。Cancel() は、低レベルI/Oの通知のみに使用され、何も実行しない場合もあります。ストリーミングデバイスはどの転送がキャンセルされるべきかを認識しているので、これらの転送をキャンセルする代わりに通常通り完了させると、完了時にフラッシュされます。すべての場合において、コールバック関数を呼び出し、ストリーミングデバイスにI/O転送情報とバッファを自由に処分しても良いことを通知する必要があります。
|
Caution: 1つの転送に対して決してコールバックを2度呼び出さないようにしてください。 |
|
Tip:
|
|
Caution: キャンセルされた転送のコールバック関数を呼び出す時、AK_Success を渡す必要があります。これ以外のすべてはI/Oエラーとみなされ、(1つまたは複数の)関連ストリームが終了します。 |
|
Caution: AK::StreamMgr::IAkIOHookDeferred::Cancel() はどのスレッドからも呼び出し可能です。結果として、AKStreamMgr::IAkIOHookDeferred::Cancel() を実装する場合は、低レベルI/Oのロックインに非常に慎重になる必要があります。具体的には、Cancel() からの pCallback のコールバックおよび通常のI/O完了コードパスからのコールバック間の競合状態を回避しなければなりません。 更なる詳細は、関数の解説に記載されています。 |
|
Tip: AK::StreamMgr::IAkIOHookDeferred::Cancel() を、その実装目的のためだけに実装しないでください。ロッキング問題のために、通常どおりの完了より要求のキャンセルのほうが損害が大きくなることがあります。 |
前に述べたように、Stream Manager ユーザーは低レベルI/Oによる許容転送サイズへの制約を考慮する必要があります。最も一般的な制約は、これらのサイズがある値の倍数であるということです。この値は、任意のファイル記述子に対して AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() によって返されます。例えば、FILE_FLAG_NO_BUFFERING フラグを使用して Windows® でオープンされたファイルは、セクタサイズの倍数サイズで読み取られる必要があります。 AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() メソッドは、セクタサイズを返します。一方、Win32 ファイルがこのフラグでオープンされていない場合、AKStreamMgr::IAkLowLevelIOHook::GetBlockSize() は Stream Manager クライアントを制約しないように1を返します。
|
Caution: AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() が 0 を返すことはありません。 |
|
Tip: 低レベルブロックサイズ制約に対処する負担は Stream Manager のクライアントに渡されます。 ブロックサイズ値が大きいほど、サウンドエンジンによるストリーミングデータ浪費も多くなります。プラットフォームのI/Oシステムが特定の整列制約を持っていない場合やI/O帯域幅パフォーマンスを向上させることができない限り、低レベルブロックサイズ 1 を使用するべきです。 |
AK::StreamMgr::IAkLowLevelIOHook::GetDeviceDesc() は、Wwise でのプロファイリングに使用されます。低レベルI/Oのデフォルト実装により提供される情報が、プロファイリング時に Wwise で実際に表示されるものです。
AK::StreamMgr::IAkLowLevelIOHook::GetDeviceData() は類似していますが、すべてのプロファイルフレームで呼び出されます。 これが返す値は、Streaming Device タブの Custom Parameter カラムに表示されます。
低レベルI/Oのデフォルト実装が、Wwise SDK で提供されています。これらの実装は samples/SoundEngine/ ディレクトリにあります。
下図は低レベルI/Oサンプルおよびこれらのサンプルと低レベルI/O API との関係を示したものです。
CAkDefaultIOHookBlocking は、File Location Resolver API(AK::StreamMgr::IAkFileLocationResolver) とブロッキング(blocking)I/Oフック(AKStreamMgr::IAkIOHookBlocking)を実装します。CAkDefaultIOHookDeferred も同等のものですが、代わりに遅延(deferred )I/Oフック (AK::StreamMgr::IAkIOHookDeferred) を実装します。いずれの実装も単一デバイス I/O システムで単独使用することができます。CAkDefaultIOHookBlocking::Init() とCAkDefaultIOHookDeferred::Init() はいずれも Stream Manager にデバイス設定を渡してストリーミングデバイスを作成し、返されたデバイスIDを格納します。唯一の違いは、正常な初期化を行うために AK_SCHEDULER_BLOCKING スケジューラ型は CAkDefaultIOHookBlocking::Init() に渡す必要があるのに対し、AK_SCHEDULER_DEFERRED_LINED_UPは CAkDefaultIOHookDeferred::Init() に渡す必要があります。
また、いずれのデバイスも唯一無二の File Location Resolver として自身を Stream Manager に登録します。 ただし、これは Stream Manager に登録されている File Location Resolver が既に存在しない場合に限ります。
下図は単一デバイスのI/Oシステムを表すブロック図です。"Low-Level IO" は File Location Resolver API を実装する任意のクラスならびにI/OフックAPIのいずれかです。これは、以下のサンプルクラスのいずれかになります:
以下は、CAkDefaultIOHookBlocking を単独使用して(エラー処理なし)、I/Oシステムを初期化する方法です。
// Create the Stream Manager. AkStreamMgrSettings stmSettings; AK::StreamMgr::GetDefaultSettings( stmSettings ); AK::StreamMgr::Create( stmSettings ); // Create a streaming device with blocking low-level I/O handshaking. AkDeviceSettings deviceSettings; AK::StreamMgr::GetDefaultDeviceSettings( deviceSettings ); deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_BLOCKING; CAkDefaultIOHookBlocking lowLevelIO; // Init registers lowLevelIO as the File Location Resolver if it was not already defined, and creates a streaming device. lowLevelIO.Init( deviceSettings );
代わりに CAkDefaultIOHookDeferred デバイスを使用したい場合は、CAkDefaultIOHookBlocking を CAkDefaultIOHookDeferred に、AK_SCHEDULER_BLOCKING を AK_SCHEDULER_DEFERRED_LINED_UP に置き換えてください。
これら両デバイス内への File Location Resolver 実装は、これらの継承元となる CAkFileLocationBase のサービスを使用します。CAkFileLocationBase に実装されているファイルロケーション ストラテジーについては、下記の 基本的なファイルロケーション を参照してください。
デフォルトのブロッキングI/Oフックおよび遅延I/Oフック実装に関する詳細は、下記の ブロッキングI/Oフック チュートリアル と遅延I/Oフック チュートリアル を参照してください。
テンプレートとして記述されている別のクラスがあり、これは AK::StreamMgr::IAkFileLocationResolver と AK::StreamMgr::IAkLowLevelIOHook(CAkDefaultIOHookBlocking とCAkDefaultIOHookDeferred など)の両方を実装するクラスにファイルパッケージを管理する機能を追加します。これは、 CAkFilePackageLowLevelIO<> です。ファイルパッケージとは、AK File Packager ユーティリティを使用して作成されたファイルのことです。低レベル I/Oでのファイルパッケージ処理に関する詳細は、以下の サンプルファイルパッケージ 低レベルI/O 実装チュートリアル セクションを参照してください。 CAkFilePackageIOHookBlocking および CAkFilePackageIOHookDeferred クラスは、CAkDefaultIOHookBlocking と CAkDefaultIOHookDeferred の具体的な定義で、それぞれファイルパッケージ管理で強化されています。
複数のデバイスを接続したI/Oシステムを実装したい場合は、適切なデバイスにファイル管理をディスパッチするタスクを持つ個別の File Location Resolver を Stream Manager に登録する必要があります。SDK は、この機能を実装するためのキャンバスを提供しています:CAkDefaultLowLevelIODispatcher。マルチデバイスI/Oシステムに関する詳細は、マルチデバイスI/Oシステム を参照してください。
サウンドエンジンに使用されるファイルは、ID(ストリーミングオーディオファイルとサウンドバンク両方)または文字列(一般的にサウンドバンクのみ)で開かれます。CAkDefaultIOHook[Blocking|Deferred] は、グローバルパス (SetBasePath()、SetBankPath()、 SetAudioSrcPath()) を設定するメソッドを公開する CAkFileLocationBase から継承します。CAkDefaultIOHook[Blocking|Deferred]::Open() のオーバーロードはいずれも、CAkFileLocationBase::GetFullFilePath() を呼び出し、ネイティブファイルオープン関数で使用できる完全なファイル名を作成します。基本パスが最初に付加されます。 続いて、ファイルが SoundBank の場合は、SoundBank パスが追加されます。ストリーミングオーディオファイルの場合は、オーディオソースパスが追加されます。いずれの場合も、現在の言語に依存するロケーションを持つファイルであれば、言語ディレクトリ名が追加されます。
文字列オーバーロードが使用されると、ファイル名の文字列がこのパスに付加されます。
ID のオーバーロードでは、Audiokinetic のファイルIDのみが解決されます。IDオーバーロードを使用するゲームは、そのIDマッピングスキームに従って実装を変更する必要があります。CAkFileLocationBase のマッピングスキームは以下のとおりです:ファイルIDに基づいて文字列を作成し、(コーデックIDで指定される)ファイルタイプに依存する拡張子を追加。これは、サウンドバンク設定の生成後ステップ "Copy Streamed Files(ストリームファイルをコピー)" で使用されるストリーミングファイル命名規則と互換性があります。 サウンドバンク設定の詳細については、Wwise Help を参照してください。
|
Note: SoundBank 設定の "Use SoundBank names(SoundBank名を使用)" オプションが選択されていない場合、Wwiseは[ID].bnk 形式の名前を持つバンクファイルを生成します。従って、(AKSoundEngine::LoadBank()のIDオーバーロードを介した)IDによる明示的バンクロードと AK::SoundEngine::PrepareEvent() からトリガーされる暗黙的バンクロードが、Default Low-Level I/O(デフォルト低レベルI/O)に適切にマッピングされます。"Use SoundBank names" が選択されている場合、Wwise は元の名前(bank_name.bnk)でバンクファイルを生成します。文字列による暗黙的なバンクローディングと明示的なバンクローディングが適切にマッピングされます。しかし、Default Low-Level I/O は、[ID].bnk という名前のファイルを開こうとしますが、このファイルは存在しないためIDによる明示的なバンクローディングは動作しません。 SDK的観点による "Use SoundBank names" オプションに関する議論は SoundBank 名の使用 を、一般的な SoundBank 設定に関しては、Wwise Helpを参照してください。 同様に、Default Low-Level I/O は、[ID].[ext] 形式の名前を持つストリームオーディオファイルを開きます。[ext] は、オーディオ形式に依存する拡張子です。SoundBank 生成の最後に Wwise が Generated SoundBank パスにすべてのストリームオーディオファイルを [ID].[ext] ファイル名形式で自動的にコピーするよう指示することが可能です(ストリーミングオーディオファイル とWwise Help参照)。 |
完全なファイルパスが取得されると、 CAkDefaultIOHook[Blocking|Deferred]::Open() が(プラットフォーム固有サンプルファイル AkFileHelpers.h に実装されているヘルパーでラップされた)システム API を使用してファイルを直接開きます。
ゲームコードから、上記の CAkFileLocationBase メソッドを使用して、ベース、SoundBank、オーディオソースと言語固有パスを設定できます。詳細は、デフォルト低レベル I/O の実装 のサンプルコードを参照してください。
|
Tip: サウンドエンジンは、言語固有ディレクトリからいつバンクがロードされるべきかを認識しないため、まず AkFileSystemFlags 構造体の bIsLanguageSpecific フラグを true に設定した状態で、常に AK::IAkStreamMgr::CreateStd() を呼び出します。 もしこの呼び出しが失敗すると、フラグを false に設定して再度呼び出しを実行します。低レベルI/Oのサンプルデフォルト実装は、自動的に現在の言語固有ディレクトリからファイルを開こうとしますが、fopen() への呼び出し失敗は避けるべきなので、これはもちろん非効率的です。 ニーズに合うように常に低レベルI/Oを再実装する必要があります。言語固有の SoundBanks 名を知っている場合や、これらを識別するための命名法を定義している場合は、プロセス早期に正しいフォルダからこれらをロードしてください。 |
io_bSyncOpen フラグに関する解説は 遅延オープン を参照してください。
すべてのデフォルト I/O フック(ブロッキングと遅延)は in_bAsyncOpen フラグで初期化されます(CAkDefaultIOHook[Blocking|Deferred]::Init() 参照)。これが true の場合、可能な場合にファイルが非同期的にオープンされます。つまり、in_bAsyncOpen が true かつ AK::StreamMgr::IAkFileLocationResolver::Open() の io_bSyncOpen 関数が false の場合、ファイルのオープンが遅延します。この場合、AkFileDesc 構造体の deviceID フィールドのみが設定されており、メソッドは直ちに戻ります。
すべてのプラットフォームにおいて、CAkDefaultIOHookBlocking::Read()とCAkDefaultIOHookBlocking::Write() の実装は、AkIOTransferInfo で提供される情報を使用して、ネイティブファイルの read/write 関数に対してブロッキングコールを実行します。 操作が成功した場合、関数は AK_Success を返します。
遅延(deferred)I/Oフックの実装は、ブロッキング(blocking)I/Oフックの実装よりやや複雑です。一般的に、プラットフォーム上の非同期ファイル読み込み API は、プラットフォーム固有の構造体を fread()(Windows 上の OVERLAPPED)へ渡し、コールバック関数が I/O の完了を通知するために呼び出されるまで、I/O 操作の全存続期間中これを保持するよう要求します。
実装は、すべてのプラットフォームにおいて多少類似しています。CAkDefaultIOHookDeferred は独自のメモリプール内でこれらのプラットフォーム固有構造体の配列を割り当てます。CAkDefaultIOHookDeferred::Read() が呼び出されると、これは、フリー(free)である最初の構造体を見つけて、この構造体を "used(使用済み)" と指定し、これを AkAsyncIOTransferInfo で提供される情報で埋めてから fread() に渡します。また、シグネチャがプラットフォームの非同期 fread() 関数と互換性を持つローカルスタティックコールバック関数を渡します。この関数では、操作が成功したかどうかを決定し、プラットフォーム固有 I/O 構造体を解放し、ストリーミングデバイスをコールバックします。
Read()/Write() とシステムコールバック間の競合状態を避ける必要があるので、配列からのプラットフォーム固有 I/O 構造体の取得と解放は、アトミックでなければなりません。
一部のプラットフォームは、カーネルに既に送信されたI/O要求をキャンセルするサービスを公開します。その場合、これは CAkDefaultIOHookDeferred::Cancel() 内から呼び出されます。Windowsでは、CancelIO() が任意のファイルハンドルに対する全ての要求をキャンセルします。従って、この関数を呼び出す前に io_bCancelAllTransfersForThisFile 引数を見て、ストリーミングデバイスが指定されたファイルに対する全ての要求をキャンセルしようとしていることを確認する必要があります。そうでない場合は、Cancel() は何も実行しません:要求が完了するまでただ待機するのみです。ストリーミングデバイスは、破棄する必要のあるものを識別します。
|
Caution: ストリーミングデバイスによって明示的にキャンセルされなかった要求をキャンセルしないでください。もしこれを行ってしまった場合、無効または壊れたデータによるフィードにつながり、サウンドエンジンをクラッシュさせる可能性があります。 |
|
Caution: CAkDefaultIOHookDeferred は、ネイティブ非同期ファイル読み込み関数を使用し、実際に独自の遅延I/Oフックを実装するためのキャンバスとしてのみ提供されています。これをそのまま使用することはお勧めしません。ネイティブのブロッキング呼び出しより優れたパフォーマンスを得ることができません。実際、パフォーマンスが低下することもあります。これについては、遅延I/Oフック セクションで説明しています。遅延低レベル API の目的は、独自のストリーミング技術を使用してより効率的に I/O要求を処理できるようにすることです。 ブロッキングI/Oフックの代わりに遅延I/Oフックを使用 は、適切なフックの種類を決定するための手引きになるでしょう。 |
下図は、マルチデバイスI/Oシステムを表すものです。
複数のストリーミングデバイスを使用して作業するために必要なことは、デバイスの低レベルI/Oフックと異なる File Location Resolver をインスタンス化および登録することです。 このオブジェクトの目的は、適切なデバイスにファイルをディスパッチすることです。 どのデバイスがどのファイルを処理するかを決定するために使用するストラテジーは各自で定義できます。 CAkDefaultLowLevelIODispatcher をキャンバスとして使用することが可能です。デフォルト実装は、ブルートフォースを使用しています:各デバイスはそのいずれかが成功するまでファイルを開くよう要求されます。従って、これらのデバイスは、 AKStreamMgr::IAkFileLocationResolver インターフェースも実行する必要があります。 これは、SDK で提供されるサンプルのいずれかになります:
以下は、マルチデバイスシステムで遅延(deferred)デバイスとファイルパッケージブロッキング(blocking)デバイスの両方のインスタンスを作成する方法です(ただし、この例には実用性はありません)。
// Create Stream Manager. AkStreamMgrSettings stmSettings; AK::StreamMgr::GetDefaultSettings( stmSettings ); AK::StreamMgr::Create( stmSettings ); // Create and register the File Location Resolver. CAkDefaultLowLevelIODispatcher lowLevelIODispatcher; AK::StreamMgr::SetFileLocationResolver( &lowLevelIODispatcher ); // Create deferred device. CAkDefaultIOHookDeferred hookIODeferred; AkDeviceSettings deviceSettings1; AK::StreamMgr::GetDefaultDeviceSettings( deviceSettings1 ); deviceSettings1.uSchedulerTypeFlag = AK_SCHEDULER_DEFERRED_LINED_UP; hookIODeferred.Init( deviceSettings1 ); // Add it to the global File Location Resolver. lowLevelIODispatcher.AddDevice( hookIODeferred ); // Create a blocking device with file package management. CAkFilePackageIOHookBlocking hookIOBlockingFP; AkDeviceSettings deviceSettings2; AK::StreamMgr::GetDefaultDeviceSettings( deviceSettings2 ); deviceSettings2.uSchedulerTypeFlag = AK_SCHEDULER_BLOCKING; hookIOBlockingFP.Init( deviceSettings2 ); // Add it to the global File Location Resolver. lowLevelIODispatcher.AddDevice( hookIOBlockingFP );
|
Tip: 複数デバイスは、複数の物理デバイスとのみ使用してください。 |
CAkFilePackageLowLevelIO<> クラスは、デフォルト低レベルI/Oフックの上にあるレイヤーです。このクラスは、サンプル File Packager により生成されたファイル(File Packager ユーティリティ 参照)をロードできることにより、デフォルト低レベルI/Oフックを拡張します。 ID をファイル記述子へ解決するためには、より高度なストラテジーを使用します。ファイルパッケージは、たくさんの連結されたファイル(ストリーミングオーディオファイルとバンクファイル)で構成されており、ヘッダーにはこれらのファイルに関する情報が含まれています。
サンプルコードは ファイルパッケージ低レベルI/Oの実装 を参照してください。File Package Low-Level I/O を、これに対応する File Packager ユーティリティと一緒に “そのまま” (CAkFilePackageLowLevelIO[Blocking|Deferred]) 使用することが可能です。またこれを、高度なファイルロケーション解決メソッド実装のための概念実証とみなすことも可能です。
File Package Low-Level I/O は、CAkFilePackageLowLevelIO::LoadFilePackage() メソッドを公開しますが、その引数はサンプル File Packager で生成されたパッケージの名前です。 File Package Low-Level I/O は、デフォルト実装のサービスを使用してパッケージを開き、ヘッダーを解析してルックアップテーブルを構築します。ファイルパッケージは、必要な数だけロードすることができます。LoadFilePackage() は、アンロードのために UnloadFilePackage() と使用できるIDを返します。
CAkFilePackage クラスは、ロードされたファイルパッケージを表し、ファイルのルックアップを処理するデータ構造とコードは CAkFilePackageLUT クラスで定義されています。 CAkFilePackageLowLevelIO<> クラスは、デフォルトI/Oフックメソッドの一部をオーバーライドして、CAkFilePackageLUT のルックアップサービスを呼び出します。ファイル記述子が見つからない場合、あるいは、要求がファイルパッケージに属しているファイル記述子に関係しない場合は、デフォルト実装が呼び出されます。
パッケージ内ファイルのルックアップの背後には、次のような考え方があります:プラットフォームのファイルオープン関数から一度だけファイルハンドルを取得し、AKStreamMgr::IAkFileLocationResolver::Open()への呼び出しのたびに、このファイルハンドルを使用するファイル記述子を、(AkFileDesc ファイル記述子の uSector フィールドを使用して)ファイルパッケージ内の元のファイルのオフセットに対応するオフセットとともに返すだけでよい。 またこれには、ディスク上ファイルの配置を細かく制御できるという利点もあります。
File Packager ユーティリティは慎重にヘッダーを準備し、Low-Level I/O(低レベルI/O)がパッケージに含まれるルックアップテーブルを取得するためのポインタをいくつか、つまりストリーミングオーディオファイル用に1つ、バンクファイル用に1つ、キャストできるようにします。ルックアップテーブルは、次の構造体の配列です:
struct AkFileEntry { AkFileID fileID; // File identifier. AkUInt32 uBlockSize; // Size of one block, required alignment (in bytes). AkInt64 iFileSize; // File size in bytes. AkUInt32 uStartBlock;// Start block, expressed in terms of uBlockSize. AkUInt32 uLanguageID;// Language ID. AK_INVALID_LANGUAGE_ID if not language-specific. };
テーブルキーは、ファイルの fileID です。異なる言語の対応ファイルも同じ fileID を持ちますが、異なる uLanguageID を持ちます。File Packager は常にまず fileID でファイルエントリをソートし、次に uLanguageID でソートします。CAkFilePackageLowLevelIO::Open() では、ID が CAkFilePackageLUT::LookupFile() に渡されます(Open() の文字列バージョンでは、サウンドエンジン API のサービス AK::SoundEngine::GetIDFromString() を使用してまず文字列がハッシュされます)。 CAkFilePackageLUT::LookupFile() は、フラグの uCodecID に基づいて検索対象の適切なテーブルを選択し、fileID および uLanguageID キーでバイナリ検索を実行します。一致が見つかった場合は、ファイルエントリのアドレスが CAkFilePackageLowLevelIO::Open() に返され、これはファイル記述子(AkFileDesc)を埋めるための必要情報を収集します。
|
Tip: 各ファイルパッケージは、一致するものが見つかるまで検索されます。サウンドバンクのみのファイルパッケージ、または、ストリーミングファイルのみのファイルパッケージを使用する場合は、AkFileSystemFlags を使用して適切なファイルパッケージ内でファイルを一度のみルックアップするよう実装を変更することが可能です。 |
ファイル記述子 hFile のハンドルは、ファイルパッケージのものです。ファイルサイズ iFileSize とスターティングブロック uSector は、ファイルエントリに直接格納されています。
|
Note: Stream Manager は、ハンドル hFile によって表されるファイルの先頭からのバイトオフセットを求めず、むしろブロック単位のオフセット(“セクタ”)を求めるということを思い出してください。ブロックサイズは、ファイル位置の粒度を表します。File Packager の現在のバージョンは、すべてのファイルに対して同じブロックサイズを使用しますが、これは生成時に指定されます(-blocksize switch を使用- File Packager のコマンドライン引数に関する詳細は、Wwise Help 参照)。連結されたファイルが常にブロック境界から開始するよう、File Packager は、ゼロパディングを実行します。 |
File Package low-level I/O は、ブロックサイズを格納するためにファイル記述子の uCustomParamSize フィールドを使用します。これには2つの目的があります:
Wwise バージョン2011.2以降では、AkStreamMgrModule.h で定義されている AK::StreamMgr::SetCurrentLanguage() を使用して、現在の言語がデフォルト Stream Manager モジュールに設定されています。末尾のスラッシュまたはバックスラッシュを付けずに言語名を渡してください。
CAkFileLocationBase から継承するデフォルトの低レベルI/O実装は、Stream Manager から言語名を取得し、これをベースパスに追加します。従って、言語名はこの特定言語のローカライズ済みアセットが格納されているディレクトリの名前に対応している必要があります。
File Packager ユーティリティによって生成されたファイルパッケージには、異なる言語による同一アセットの1つまたは複数バージョンが含まれている可能性があります。 ヘッダーには、言語名の文字列マップが含まれています。File Package Low-Level I/O は、Stream Manager における言語変更をリスンし、現在の言語名を使用して、パッケージ化されたローカライズアセットの適切なローカライズバージョンをルックアップします。