バージョン

menu_open
Wwise SDK 2024.1.0
低レベル I/O
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つの目的があります:

  • ファイルロケーションの解決(リゾルブ)。
  • 実際のI/O転送を抽象化。

File Location Resolver(ファイルロケーションリゾルバ)と呼ばれる AK::StreamMgr::IAkFileLocationResolver を実装する唯一のオブジェクトを、(AK::StreamMgr::SetFileLocationResolver() を使用して)Stream Manager に登録する必要があります。自動ストリームまたは標準ストリームを開くために、Stream Managerが AK::StreamMgr::IAkFileLocationResolver::GetNextPreferredDevice() を呼び出してファイルを持っている可能性の高いストリーミングデバイスを探します。ストリーミングデバイスは、AK::StreamMgr::CreateDevice() を使用して Stream Manager にて作成および登録されます。

各ストリーミングデバイスには、低レベルI/Oフックが提供される必要があります。このI/Oフックが AK::StreamMgr::IAkLowLevelIOHook を実装してプラットフォームのI/O APIとのコミュニケーションを担い、その物理デバイス固有のプロパティと動作に対応します。 ストリーミングデバイスはI/O転送を実行する必要がある場合、低レベルI/Oフックの AK::StreamMgr::IAkLowLevelIOHook::BatchRead() または AK::StreamMgr::IAkLowLevelIOHook::BatchWrite() メソッドを呼び出します。High-Level Stream ManagerからプラットフォームのI/O APIに呼出しが直接発行されることはありません。File Location ResolverとI/Oフックは、ともにLow-Level 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はストリームオブジェクトを作成するたびにリゾルバのGetNextPreferredDevice()メソッドを呼び出しますが、これによってファイルを開くために使うI/O Deviceを特定するAkDeviceIDが返されるはずです。この関数の目的はファイルの場所を見つけるために名前、フラグ、言語などのみを使用し、ディスク処理やその他の物理チェックは実行しないことです。実際のOpenコールは指定したデバイスに転送され、ディスク処理が実行されます。そのため、この関数は迅速に情報を返すことが求められます。 Deviceの検索やファイルのオープンに失敗するとGetNextPreferredDeviceが再び呼び出されます。他にDeviceが見つからない場合やファイルが他の場所で見つかる見込みがない場合 、AK_FileNotFoundが返され検索の終了を通知します。

Stream Manager 内にあるストリームオブジェクトと同数のファイルが同時に低レベル I/Oに存在します。

ゲームは、AK::StreamMgr::CreateDevice() を使用して、少なくとも1つのストリーミングデバイスを Stream Manager に作成する必要がありますが。 返されたAkDeviceIDを、呼び出し元は保管し、 AK::StreamMgr::GetNextPreferredDevice の際は前述の通りこれが返されます。 必要に応じていくらでもデバイスを作成することができます。各ストリーミングデバイスは、独自スレッドで実行され、I/O要求を独自I/Oフックに送信します。一般的に、物理デバイス1つにつきストリーミングデバイスを1つ作成する必要があります。 AkFileDesc 構造体には、deviceID と呼ばれるフィールドがあります。File Location Resolverは、これを作成されたストリーミングデバイスのいずれかの deviceID に設定しなければなりません。このようにして、ファイルハンドリングが適切なデバイスにディスパッチされます。さらにファイルのサイズとオフセット( uSector )が返され、システムファイルハンドルが作成される必要があります(これは必須ではありません)。

ファイル記述

Stream Managerのクライアントは、文字列(const AkOSChar *)、ID(AkFileID, integer)またはファイル記述子( AkFileDesc )を使用してファイルを識別する必要があります。そのためStream Managerのストリーム作成メソッド( AK::IAkStreamMgr::CreateStd()AK::IAkStreamMgr::CreateAuto() )は構造体コール AkFileOpenData を使用し、可能性のある両方のファイル識別メソッドをグループ化します。 一旦ファイルが開かれると、 AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() メソッドは有効なファイル記述子を作成する必要があります。

このファイル記述子は、低レベルI/Oフックのメソッドへの呼び出しのたびに戻されます。構造体の3つのメンバが高レベル Stream Manager によって使用されます:

  • deviceID:AK::CreateDevice() への呼び出しから取得される有効なデバイスID。Stream Manager によって、ファイルを適切な高レベルデバイスに関連付けるために使用されます。関連付けが完了すると、デバイス作成時に渡されたI/Oフックを介してI/O転送が実行されます。
  • uSector:記述されたファイルの先頭オフセット。AkFileHandle AkFileDesc::hFile で表されるファイルの先頭に対して相対的。このファイル記述子に対して AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() によって返される値に対応するサイズを持つブロック(セクタ)によって表されます。 ストリーミングデバイスは、I/O転送のためのメソッドを呼び出す時に、ファイルの先頭からのオフセット(バイト単位)を AkTransferInfo 構造体の一部として低レベルI/Oフックに送信します。オフセットは、次のように計算されます:Current_Position + ( AkFileDesc::uSector * Block_Size)。ブロックサイズも、各ファイル記述子ごとに1回、このフックを介してクエリされることに注意してください。
  • iFileSize:ファイルの末尾を検出するために Stream Manager によって使用されます。検出結果を(AK::IAkStdStream::GetPosition(), AK::IAkAutoStream::GetPosition() を介して)ユーザーにレポートし、自動ストリームのI/O転送を停止します。

残りのメンバは、低レベルI/Oシステムが排他的に所有します。例えば、Win32 で HANDLE にタイプ定義されている AkFileHandle は、Win32 の ReadFile() に渡された実有効ファイルハンドルを保持するために使用することができます。一方、これをIDまたはポインタとして使用することも可能です。高レベルデバイスがファイル記述子フィールドを変更または読み取ることはありません。そのため、ファイルがゲームディスク上に重複してレイアウトされている場合などに、低レベルI/Oはファイルハンドルを自由に閉じて開きなおすことができます。

AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() で返されたファイル記述子構造は、 AK::StreamMgr::IAkLowLevelIOHook::Close() が呼び出されるまで、関連するストリームオブジェクトの存続期間を通じて変わりません。

遅延オープン

AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() によるすべてのファイルオープン操作は、同期または遅延させることができます。 デバイスのネイティブAPIが遅延オープンをサポートしている場合、パフォーマンス上の理由から、これを使用することが常に推奨されます。これによって一部のシステムでは、ファイルオープン操作とリード/ライト操作を並行または優先順位を変えて実行させることが可能になります。

Open() のパラメータは AkFileOpenData から派生した AkAsyncFileOpenData 構造体で、 AK::IAkStreamMgr::CreateStd() 関数または AK::IAkStreamMgr::CreateAuto() 関数で渡されたすべての情報が含まれます。またOpen操作の完了を高レベルStreaming Managerに通知するために必要なコールバック関数と、入力して返す AkFileDesc 構造体が含まれます。

All calls to AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() are considered asynchronous, from the Stream Manager perspective. Once the open operation is resolved, signal the result of the operation through the notification callback (io_pOpenData->pCallback). It is imperative to notify of the result once it is known, otherwise the stream object waiting for this file will stay alive indefinitely and cause a memory leak. If the result reported by io_pOpenData->pCallback is AK_Success, it is expected that io_pOpenData->pFileDesc is filled properly and usable. Note that it is supported to call io_pOpenData->pCallback right away (synchronously) if the result of the operation is known at the moment of the BatchOpen() call.

Tip: The AkAsyncFileOpenData (io_pOpenData) parameter that is passed to AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() will stay valid until the notification io_pOpenData->pCallback is received.

If the file is opened successfully, set in_eResult to AK_Success and create the file descriptor structure (io_pOpenData->pFileDesc) properly. The pFileDesc member must point to memory that will be valid for the lifetime of the stream object, until Close() is called. Use AK_FileNotFound if the file was not found and set io_pOpenData->pFileDesc to null. Other error codes are possible, such as AK_FilePermissionError, AK_FilePathTooLong or AK_UnknownFileError.

ファイルシステムフラグ

Stream Managerの AK::IAkStreamMgr::CreateStd()AK::IAkStreamMgr::CreateAuto() メソッドは、 AkFileSystemFlags 構造体へのポインタを受け取り、これは AK::StreamMgr::IAkFileLocationResolver::GetNextPreferredDevice()AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() へ渡されます。この構造は、ユーザーから低レベルI/Oへ直接情報を渡すための方法です。この情報は、例えばファイルロケーションのロジックを完了するために使用されています。Stream Manager の汎用ユーザーは NULL を渡すことができますが、File Location Resolver がサウンドエンジンから要求が来ることを認識できるよう、サウンドエンジンは常に関連情報が入った構造体を渡します。

ファイルシステムのフラグ構造体には以下のフィールドが含まれます:

  • uCompanyID:サウンドエンジンはこのフィールドを常に AKCOMPANYID_AUDIOKINETIC に設定し、このファイルがサウンドエンジンに読み取られなければならないことを低レベルI/O実装者が認識できるようにします。 ストリーミングされた外部ソースの場合、サウンドエンジンは、AKCOMPANYID_AUDIOKINETIC_EXTERNALを渡します。
  • uCodecID:このフィールドはファイルタイプの識別に使用されます。これは、サウンドエンジンに使用されるファイルタイプのために必要になります。サウンドエンジンに使用されるコーデックIDは、AkTypes.h で定義されます。ホストプログラムも、companyID を AKCOMPANYID_AUDIOKINETIC または AKCOMPANYID_AUDIOKINETIC_EXTERNAL に設定していない限り、同じ値を使用して独自のIDを定義することができます。 なお従来のCodecに加え、事前定義されたCodecIDであるAKCODECID_BANK、AKCODECID_BANK_EVENT、AKCODECID_BANK_BUSをファイルの整理に活用できます。
  • bIsLanguageSpecific:このフィールドは、参照されるファイルが現在の言語に固有のものであるかどうかを示します。一般的に、言語固有コンテンツを持つファイルは、別の場所(通常は言語フォルダ)に存在します。低レベルI/Oは、現在選択されている言語に応じてパスを解決する必要があります。詳細は、言語固有の ("Voice" および "Mixed") Soundbanks を参照してください。
  • Custom parameter and size(カスタムパラメータとサイズ):ロケーションスキームのゲーム固有拡張子に使用されます。例えば、拡張子をファイル記述子にコピーすることができます。サウンドエンジンは常に0を渡します。

サウンドエンジンファイルの解決

サウンドエンジンは、SoundBank ファイルとストリームオーディオファイルを読み込みます。このサブセクションでは、低レベルI/O がその識別子を実際のファイルに解決する方法について説明します。

AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() で受け取ったIDをLow-Level I/Oの有効なファイル記述子にマッピングするための戦略として、次のいずれかが可能です:

  • IDのマップをファイル名に実装して使用。
  • IDからファイル名の文字列を作成。
  • ストリームオーディオファイルを連結する大きなファイルのシステムハンドルを取得し(例えば、File Packager アプリケーションで生成されたファイルパッケージ – 詳細は Wwise Help を参照)、この大きなファイル内のファイルのサイズやオフセットを定義するファイル記述子構造体にIDのマップを実装して使用。

低レベル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フラグを無視してサウンドバンクを正しい場所で直接開きます。サウンドバンクの内容と位置を把握しているのはユーザのみとなります。また AK::SoundEngine::LoadBank() の非同期バージョンに渡されたクッキーは、in_pFlags->pCustomDataの値として AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() に渡されます。バンクが言語固有ディレクトリから開かれるべきかどうかを判断するためにこれを使用することができます。

ストリーミングオーディオファイル

ストリーミングファイルのリファレンスは、整数IDとしてバンクに格納されています。ストリーミングされることになっている変換されたオーディオファイルの実ファイルパスは、バンクと一緒に生成される SoundBanksInfo.xml ファイルに入っています(詳細は、SoundBanksInfo.xml を参照してください)。

Tip: WwiseのSoundBank 設定で "Copy streamed files(ストリーミングファイルをコピーする)"を選択すると、スキームの [ID].[ext] に改名して特定プラットフォームの Generated SoundBanks(生成されたサウンドバンク)フォルダにコピーすることができます。 詳細については ストリーミングオーディオファイル を参照して下さい。 デフォルトファイルロケーション実装( CAkFileLocationBase )は、"Copy Loose/Streamed Media"オプションで使用されることを想定しています。

サウンドエンジンはストリーミングオーディオファイルを再生しようとする時、AK::IAkStreamMgr::CreateAuto() の IDオーバーロードを呼び出します。これは AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() のIDオーバーロードまで伝達されます。IDに加えて、次の情報を含む AkFileSystemFlags 構造体が渡されます:

  • uCompanyID は AKCOMPANYID_AUDIOKINETIC である。
  • uCodecID は、AkTypes.h (AKCODECID_XXX) で定義されているオーディオフォーマットの1つである。
  • 現在のゲーム言語に依存するロケーションでファイルを検索する必要がある時に true、それ以外の場合には false である bIsLanguageSpecific フラグ。 By default, loading soundbanks query the language-specific folder first.

I/O転送インターフェース

Once file location has been resolved through AK::IAkFileLocationResolver::GetNextPreferredDevice(), the Stream Manager then calls AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() on the selected streaming device, which interacts with the Low-Level I/O system through its own I/O hook. AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() への呼び出しにより、低レベルブロックサイズによる制約がまず最初にクエリされます。 Then, every input data transfer is executed through the hook's BatchRead() method, and output through the hook's BatchWrite() method. When the stream is destroyed, AK::StreamMgr::IAkLowLevelIOHook::Close() is called.

これらのすべてのメソッドには、File Location Resolver によって充填されたものと同じファイル記述子が渡されます。

高レベルデバイスの特異性

現在のStream Managerの実装は、非同期ハンドシェークをLow-Level I/Oと使用する1種類のI/Oフックを定義します。

AK::StreamMgr::CreateDevice() は、File Location Resolver によってファイルディスクリプタ(記述子)構造体に設定されているデバイスIDを返します。

以下のセクションでは遅延(deferred)I/Oフックについて説明します。

遅延I/Oフック

Stream Managerは、 AK::StreamMgr::IAkLowLevelIOHook インターフェースを介してLow-Level I/Oシステムとやり取りするストリーミングデバイスを作成します。

Low-Level I/O implementations must handle multiple transfer requests at the same time. The interface defines a few important methods: AK::StreamMgr::IAkLowLevelIOHook::BatchOpen(),AK::StreamMgr::IAkLowLevelIOHook::BatchRead() and AK::StreamMgr::IAkLowLevelIOHook::BatchWrite(). BatchRead()とBatchWrite()はすぐに返し、1つ以上の転送が完了したら、提供されたコールバック関数を介してストリーミングデバイスに通知するべきです。See details about AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() in the section 遅延オープン.

ストリーミングデバイスが低レベルI/Oに送信できる同時I/O転送の最大数を、その初期設定( AkDeviceSettings::uMaxConcurrentIO )で設定します。

For each transfer request sent to AK::StreamMgr::IAkLowLevelIOHook::BatchRead() or AK::StreamMgr::IAkLowLevelIOHook::BatchWrite(), the provided callback function in AkAsyncIOTransferInfo must be called once, with the result of the transfer. It must be called even in failure cases, otherwise the engine will wait for this transfer forever. The callback can be deferred to later, as it is common for asynchronous I/O subsystems to execute the transfer in the background.

The in_eResult parameter of the callback should be AK_Success if the transfer succeeds or any other error code if it fails. If any transfer is marked as AK_Fail, the corresponding stream will be destroyed, and an "I/O error" notification will appear in the transfer log.

注釈: AkDeviceSettings::uMaxConcurrentIO は、デバイスが低レベルI/Oに送出する転送要求の最大数を表します。デバイスのスケジューラは、Stream Managerのクライアントが AK::IAkStdStream::Read()/Write() を呼び出した場合、または実行中の自動ストリームのバッファリングがバッファリングターゲット( AkDeviceSettings::fTargetAutoStmBufferLength 、ターゲットバッファリング長の詳細は Audiokinetic ストリームマネージャ初期化設定 を参照)以下の場合のみ、転送要求の送出を決定します。

ストリーミングデバイスがBatchIoTransferItemのアレイを AK::StreamMgr::IAkLowLevelIOHook の各関数に渡します。 転送ごとの情報がこの構造体に入れられ、 AkFileDescAkIoHeuristicsAkAsyncIOTransferInfo などが含まれます。 AkAsyncIOTransferInfo の構造体は、前述の AkIOTransferInfo の構造体を拡張します。AkAsyncIOTransferInfo にReadやWriteに使うバッファのアドレスが含まれ、実装者がペンディング中の転送にメタデータを添付しやすいように pUserData フィールドが提供されます。AkAsyncIOTransferInfo 構造体は、コールバックが呼び出されるまで生きています。コールバック呼出し後は、これを参照しないようにしてください。

提供される AkIoHeuristics に含まれる情報は、あなたが、I/Oリクエストの順番を変えるためにReadやWriteを自分のI/Oストリーミングテクノロジに回す場合に便利です。

Tip: デフォルト Stream Manager のスケジューラ実装は、「ディスク帯域幅ヒューリスティック」ではなく「クライアントヒューリスティック」に基づいています。Stream Manager は、ディスク上ファイルのレイアウトを認識しません。独自のストリーミング技術でレイアウト認識が可能な場合は、これを使ってI/O要求を並べ替えてディスクのシークを最小限にすることができます。
注意: 1つの転送に対して決してコールバックを2度呼び出さないようにしてください。

そのほか考慮すべきこと

ブロックサイズ (GetBlockSize())

前に述べたように、Stream Manager ユーザーは低レベルI/Oによる許容転送サイズへの制約を考慮する必要があります。最も一般的な制約は、これらのサイズがある値の倍数であるということです。この値は、任意のファイル記述子に対して AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() によって返されます。例えば、FILE_FLAG_NO_BUFFERING フラグを使用して Windows&reg でオープンされたファイルは、セクタサイズの倍数サイズで読み取られる必要があります。 AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() メソッドは、セクタサイズを返します。一方、Win32 ファイルがこのフラグでオープンされていない場合、AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() は Stream Manager クライアントを制約しないように1を返します。

注意: 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 との関係を示したものです。

CAkDefaultIOHookDeferred implements the File Location Resolver API (AK::StreamMgr::IAkFileLocationResolver) and the deferred I/O hook (AK::StreamMgr::IAkLowLevelIOHook). This implementation can be used in a single-device I/O system. CAkDefaultIOHookDeferred::Init() はStream Managerにデバイス設定を渡してストリーミングデバイスを作成し、返されたデバイスIDを格納します。

またこのデバイスは唯一無二のFile Location Resolverとして自身をStream Managerに登録します。 ただし、これは Stream Manager に登録されている File Location Resolver が既に存在しない場合に限ります。

下図は単一デバイスのI/Oシステムを表すブロック図です。"Low-Level IO" は File Location Resolver API を実装する任意のクラスならびにI/OフックAPIのいずれかです。これは、以下のサンプルクラスのいずれかになります:

  • CAkDefaultIOHookDeferred
  • CAkFilePackageIOHookDeferred

以下は CAkDefaultIOHookDeferred を単独使用して(エラー処理なし)I/Oシステムを初期化する方法です。

// Create the Stream Manager.
AkStreamMgrSettings stmSettings;
AK::StreamMgr::Create( stmSettings );
// ストリーミングデバイスを作成します。
AkDeviceSettings deviceSettings;
CAkDefaultIOHookDeferred lowLevelIO;
// Init registers lowLevelIO as the File Location Resolver if it was not already defined, and creates a streaming device.
lowLevelIO.Init( deviceSettings );

デバイスへのFile Location Resolverの実装は、その継承元となる CAkFileLocationBase のサービスを使用します。CAkFileLocationBase に実装されているファイルロケーション ストラテジーについては、下記の 基本的なファイルロケーション を参照してください。

デフォルトの遅延I/Oフック実装の詳細については、以下の 遅延I/Oフック チュートリアル のセクションをご覧ください。

テンプレートとして記述されている別のクラスがあり、これは AK::StreamMgr::IAkFileLocationResolverAK::StreamMgr::IAkLowLevelIOHookCAkDefaultIOHookDeferred など)の両方を実装するクラスにファイルパッケージを管理する機能を追加します。これは、 CAkFilePackageLowLevelIO<> です。ファイルパッケージとは、AK File Packager ユーティリティを使用して作成されたファイルのことです。低レベル I/Oでのファイルパッケージ処理に関する詳細は、以下の サンプルファイルパッケージ 低レベルI/O 実装チュートリアル セクションを参照してください。 CAkFilePackageIOHookDeferred クラスは CAkDefaultIOHookDeferred の具体的な定義で、ファイルパッケージ管理で強化されています。

複数のデバイスを接続したI/Oシステムを実装したい場合は、適切なデバイスにファイル管理をディスパッチするタスクを持つ個別の File Location Resolver を Stream Manager に登録する必要があります。SDK は、この機能を実装するためのキャンバスを提供しています:CAkDefaultLowLevelIODispatcher。マルチデバイスI/Oシステムに関する詳細は、マルチデバイスI/Oシステム を参照してください。

基本的なファイルロケーション

サウンドエンジンに使用されるファイルは、ID(ストリーミングオーディオファイルとサウンドバンク両方)または文字列(一般的にサウンドバンクのみ)で開かれます。CAkDefaultIOHookDeferred は、グローバルパスを設定するメソッド( SetBasePath()AddBasePath()SetBankPath()SetAudioSrcPath() )を公開する CAkFileLocationBase から継承します。Both overloads of CAkDefaultIOHookDeferred::BatchOpen() call CAkFileLocationBase::GetFullFilePath(), to create a full file name that can be used with native file open functions. 基本パスが最初に付加されます。 続いて、ファイルが SoundBank の場合は、SoundBank パスが追加されます。ストリーミングオーディオファイルの場合は、オーディオソースパスが追加されます。いずれの場合も、現在の言語に依存するロケーションを持つファイルであれば、言語ディレクトリ名が追加されます。

文字列オーバーロードが使用されると、ファイル名の文字列がこのパスに付加されます。

ID のオーバーロードでは、Audiokinetic のファイルIDのみが解決されます。IDオーバーロードを使用するゲームは、そのIDマッピングスキームに従って実装を変更する必要があります。CAkFileLocationBase のマッピングスキームは以下のとおりです:ファイルIDに基づいて文字列を作成し、(コーデックIDで指定される)ファイルタイプに依存する拡張子を追加。これは、サウンドバンク設定の生成後ステップ "Copy Streamed Files(ストリームファイルをコピー)" で使用されるストリーミングファイル命名規則と互換性があります。 サウンドバンク設定の詳細については、Wwise Help を参照してください。

注釈: SoundBank 設定の "Use SoundBank names(SoundBank名を使用)" オプションが選択されていない場合、Wwiseは[ID].bnk 形式の名前を持つバンクファイルを生成します。従って、(AK::SoundEngine::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参照)。

完全なファイルパスが取得されると、 CAkDefaultIOHookDeferred::Open() が(プラットフォーム固有サンプルファイルAkFileHelpers.hに実装されているヘルパーでラップされた)システムAPIを使用してファイルを直接開きます。

ゲームコードから、上記の CAkFileLocationBase メソッドを使用して、ベース、SoundBank、オーディオソースと言語固有パスを設定できます。詳細は、デフォルト低レベル I/O の実装 のサンプルコードを参照してください。

Tip: サウンドエンジンは、言語固有ディレクトリからいつバンクがロードされるべきかを認識しないため、まず AkFileSystemFlags 構造体の bIsLanguageSpecific フラグを true に設定した状態で、常に AK::IAkStreamMgr::CreateStd() を呼び出します。 もしこの呼び出しが失敗すると、フラグを false に設定して再度呼び出しを実行します。低レベルI/Oのサンプルデフォルト実装は、自動的に現在の言語固有ディレクトリからファイルを開こうとしますが、fopen() への呼び出し失敗は避けるべきなので、これはもちろん非効率的です。

ニーズに合うように常に低レベルI/Oを再実装する必要があります。言語固有の SoundBanks 名を知っている場合や、これらを識別するための命名法を定義している場合は、プロセス早期に正しいフォルダからこれらをロードしてください。

遅延I/Oフック チュートリアル

一般的にプラットフォーム上の非同期ファイル読み込みAPI は、プラットフォーム固有の構造体をfread()(Windows上のOVERLAPPED)へ渡し、コールバック関数がI/Oの完了を通知するために呼び出されるまで、I/O操作の全存続期間中これを保持するよう要求します。

実装は、すべてのプラットフォームにおいて多少類似しています。CAkDefaultIOHookDeferred は独自のメモリプール内でこれらのプラットフォーム固有構造体の配列を割り当てます。CAkDefaultIOHookDeferred::Read() が呼び出されると、これは、フリー(free)である最初の構造体を見つけて、この構造体を "used(使用済み)" と指定し、これを AkAsyncIOTransferInfo で提供される情報で埋めてから fread() に渡します。また、シグネチャがプラットフォームの非同期 fread() 関数と互換性を持つローカルスタティックコールバック関数を渡します。この関数では、操作が成功したかどうかを決定し、プラットフォーム固有 I/O 構造体を解放し、ストリーミングデバイスをコールバックします。

Read()/Write() とシステムコールバック間の競合状態を避ける必要があるので、配列からのプラットフォーム固有 I/O 構造体の取得と解放は、アトミックでなければなりません。

注意: Do not cancel I/O requests. もしこれを行ってしまった場合、無効または壊れたデータによるフィードにつながり、サウンドエンジンをクラッシュさせる可能性があります。

マルチデバイスI/Oシステム

下図は、マルチデバイスI/Oシステムを表すものです。

複数のストリーミングデバイスを使用して作業するために必要なことは、デバイスの低レベルI/Oフックと異なる File Location Resolver をインスタンス化および登録することです。 このオブジェクトの目的は、適切なデバイスにファイルをディスパッチすることです。 どのデバイスがどのファイルを処理するかを決定するために使用するストラテジーは各自で定義できます。 CAkDefaultLowLevelIODispatcher をキャンバスとして使用することが可能です。デフォルト実装は、ブルートフォースを使用しています:各デバイスはそのいずれかが成功するまでファイルを開くよう要求されます。従って、これらのデバイスは、 AK::StreamMgr::IAkFileLocationResolver インターフェースも実行する必要があります。 これは、SDK で提供されるサンプルのいずれかになります:

  • CAkDefaultIOHookDeferred
  • CAkFilePackageIOHookDeferred

以下はマルチデバイスシステムで遅延デバイスとファイルパッケージデバイスの両方のインスタンスを作成する方法です(ただし、この例には実用性はありません)。

// Create Stream Manager.
AkStreamMgrSettings stmSettings;
AK::StreamMgr::Create( stmSettings );
// Create and register the File Location Resolver.
CAkDefaultLowLevelIODispatcher lowLevelIODispatcher;
AK::StreamMgr::SetFileLocationResolver( &lowLevelIODispatcher );
// 1つ目のデバイスを作成します。
CAkDefaultIOHookDeferred hookIODeferred;
AkDeviceSettings deviceSettings1;
hookIODeferred.Init( deviceSettings1 );
// Add it to the global File Location Resolver.
lowLevelIODispatcher.AddDevice( hookIODeferred );
// 2つ目のデバイスをファイルパッケージ管理で作成します。
CAkFilePackageIOHookDeferred hookIOFilePackage;
AkDeviceSettings deviceSettings2;
hookIOFilePackage.Init( deviceSettings2 );
// Add it to the global File Location Resolver.
lowLevelIODispatcher.AddDevice( hookIOFilePackage );
Tip: 複数デバイスは、複数の物理デバイスとのみ使用してください。

サンプルファイルパッケージ 低レベルI/O 実装チュートリアル

概要

CAkFilePackageLowLevelIO<> クラスは、デフォルト低レベルI/Oフックの上にあるレイヤーです。このクラスは、サンプル File Packager により生成されたファイル(File Packager ユーティリティ 参照)をロードできることにより、デフォルト低レベルI/Oフックを拡張します。 ID をファイル記述子へ解決するためには、より高度なストラテジーを使用します。ファイルパッケージは、たくさんの連結されたファイル(ストリーミングオーディオファイルとバンクファイル)で構成されており、ヘッダーにはこれらのファイルに関する情報が含まれています。

サンプルコードは ファイルパッケージ低レベルI/Oの実装 を参照してください。File Package Low-Level I/Oを、これに対応するFile Packagerユーティリティと一緒に「そのまま」( CAkFilePackageLowLevelIODeferred )使用することが可能です。またこれを、高度なファイルロケーション解決メソッド実装のための概念実証とみなすことも可能です。

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 のルックアップサービスを呼び出します。ファイル記述子が見つからない場合、あるいは、要求がファイルパッケージに属しているファイル記述子に関係しない場合は、デフォルト実装が呼び出されます。

ファイルロケーション

パッケージ内ファイルのルックアップの背後には、次のような考え方があります:プラットフォームのファイルオープン関数から一度だけファイルハンドルを取得し、 AK::StreamMgr::IAkLowLevelIOHook::BatchOpen() への呼び出しのたびに、このファイルハンドルを使用するファイル記述子を、( 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 は、ファイルエントリに直接格納されています。

注釈: 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 における言語変更をリスンし、現在の言語名を使用して、パッケージ化されたローカライズアセットの適切なローカライズバージョンをルックアップします。

AKSOUNDENGINE_API void GetDefaultSettings(AkStreamMgrSettings &out_settings)
AkUInt32 AkFileID
Integer-type file identifier
Definition: AkTypes.h:77
int64_t AkInt64
Signed 64-bit integer
AKSOUNDENGINE_API IAkStreamMgr * Create(const AkStreamMgrSettings &in_settings)
AKSOUNDENGINE_API void GetDefaultDeviceSettings(AkDeviceSettings &out_settings)
AKSOUNDENGINE_API void SetFileLocationResolver(IAkFileLocationResolver *in_pFileLocationResolver)
uint32_t AkUInt32
Unsigned 32-bit integer

このページはお役に立ちましたか?

サポートは必要ですか?

ご質問や問題、ご不明点はございますか?お気軽にお問い合わせください。

サポートページをご確認ください

あなたのプロジェクトについて教えてください。ご不明な点はありませんか。

プロジェクトを登録していただくことで、ご利用開始のサポートをいたします。

Wwiseからはじめよう