cocos2d-x
Wwise

Wwise 導入日誌

More than 3 years have passed since last update.


Wwise について

Audiokinetic社 によって開発されたゲーム向け統合型サウンドミドルウェア。

Wwise は Unreal Engine や Unity などのメジャーなゲームエンジンへのインテグレーションに対応しており、実際はそのような使い方をするケースが多いとは思うが、今回は Wwise SDK を用いて Cocos2d-x のような C++ のプロジェクトに Wwise の組み込みを行う。

注意

この記事では Mac 上で Xcode と Cocos2d-x を使って実装した際の手順を挙げるが、別のプラットフォームやフレームワークを利用している場合であっても、適宜読み替えれば問題なく動作すると思われる。


開発環境


  • Mac OS X 10.10

  • Cocos2d-x 3.4 (Mac App)

  • Wwise SDK v2014.1.5_build_5282


手順


1. ヘッダ検索パスの指定

Wwise のヘッダを読み込むため、SDK 内の include ディレクトリをプロジェクトのヘッダ検索パスに追加する。

スクリーンショット 2015-06-05 8.55.42.png


2. 静的ライブラリのリンク指定

Wwise SDK 内の各プラットフォーム向け静的ライブラリをビルド時にリンクするよう設定する。

Mac の場合は (WwiseSDK)/SDK/Mac/(Configuration name)/lib の中の .a ファイル全部をリンクの対象に指定する。

ここで注意すべきなのは、プロジェクトの構成と追加するディレクトリの構成を合わせることである。

Wwise の静的リンクライブラリの構成は、それぞれ以下のようになっている。


  • Debug ... デバッグシンボルを含む、未最適化ビルド。パフォーマンスヒットが大きいため、問題の調査にのみ使用。

  • Profile ... 最適化され、かつ Wwise オーサリングと連携するための Communication サポートを含む。開発時のデフォルト。

  • Release ... 最適化され、かつ Communication サポートを含まない。リリース用。

開発時は Profile, Wwise に関する問題の解決が必要な際は Debug, リリース時は Release を使うことが想定されている。

スクリーンショット 2015-06-05 8.57.13.png


3. 低レベルIOを扱うためのコード類を追加

これはサンプルから拝借した。SDK 内の samples/SoundEngine に各プラットフォーム向けのコードが置いてある。

Mac の場合は CommonPOSIX を、そのままプロジェクトのコンパイルターゲットに追加。

このときプリコンパイル済みヘッダ stdafx.{h,cpp} を既にプロジェクト内に持っている場合、これらは wwise_stdafx.{h,cpp} とでもリネームして、既にある stdafx.h から include してやるとよいだろう。

プラットフォームによっては中身が空のため、その場合はそもそも追加しなくてもよい。

スクリーンショット 2015-06-05 9.07.24.png


4. SoundBank をリソースに追加

Wwise オーサリングから書きだした GeneratedSoundBanks の中から、ターゲットとなるプラットフォームディレクトリ内のすべての .bnk ファイルをアプリケーションにバンドルするリソースに指定する。

スクリーンショット 2015-06-05 9.09.17.png

尚、Xcode の場合は .bnk ファイルをプロジェクトに追加すると自動的に Bundle Resource に指定され、ビルド時にアプリ内に埋め込まれて、ランタイムでファイルにアクセス可能となる。

スクリーンショット 2015-06-05 9.13.08.png

尚、後述のセットアップの際に SoundBank の置いてあるパスを指定してロードすることができる。コンポーザーやサウンドエンジニアによって SoundBank が更新された際に、半自動的に更新されるような仕組みにしておけると好ましい。(もちろんプロジェクトの進め方に依るところだが……)

これを応用して、SoundBank をネットワークからダウンロードできるような仕組みを作り、SoundBank をゲームに埋め込まないようにしたり、バイナリのアップデートなしで音声のみをアップデートできるようにする、というような運用も可能。


5. Wwise_IDs.h をヘッダ検索パスに指定

Wwise オーサリング側の [Project] -> [SoundBanks] -> [Generate header file] が on のとき、.bnk ファイルと一緒に Wwise_IDs.h というヘッダファイルが生成される。

これは Wwise 内のすべてのオブジェクトに関する数値 ID を定義した特別なヘッダであり、イベントやゲームパラメータ、SoundBank、バス等をそれぞれ一意に識別する数値 ID をプログラム内で扱えるようにするために存在する。

これは以下のような単純な定義を取る。


Wwise_IDs.h

namespace AK

{
namespace EVENTS
{
static const AkUniqueID END_BATTLE = 2375763337U;
static const AkUniqueID PLAY_ATTACK = 1519321238U;
static const AkUniqueID PLAY_BGM = 3126765036U;
static const AkUniqueID START_BATTLE = 1670591260U;
} // namespace EVENTS

namespace GAME_PARAMETERS
{
static const AkUniqueID X_AXIS = 1504899761U;
static const AkUniqueID Y_AXIS = 407608950U;
} // namespace GAME_PARAMETERS

namespace BANKS
{
static const AkUniqueID INIT = 1355168291U;
static const AkUniqueID DEFAULT = 782826392U;
} // namespace BANKS

namespace BUSSES
{
static const AkUniqueID MASTER_AUDIO_BUS = 3803692087U;
static const AkUniqueID MASTER_SECONDARY_BUS = 805203703U;
} // namespace BUSSES

}// namespace AK


この定義を利用することによって、文字列によるイベントやバンクの指定などをする必要がなくなり、単純なタイプミスなどのバグを減らすことが可能である。

このファイルは SoundBank 更新時に書き換えられるため、プロジェクト内部へのコピーなどによって古い定義を保持し続けないよう注意する。

また、この数値 ID を用いて SoundBank のロードを行う場合、同じくオーサリング側の [Project] -> [SoundBanks] -> [Use SoundBank names] のチェックを外しておく必要がある。逆に名前による読み込みを行う場合、このチェックを入れておく必要がある。つまり、どちらか片方しか使えないので注意


6. プラットフォーム依存コードの追加

下記 2ファイルは SDK に同梱されている IntegrationDemo から Platform.hPlatform.cpp を参考にし、不要な行を削除したものである。これをコンパイルターゲットに追加する。


Platform.h

#pragma once

#include <AK/SoundEngine/Common/AkTypes.h>
#define __AK_OSCHAR_SNPRINTF snprintf


Platform.cpp

#include "stdafx.h"

#include "Platform.h"

namespace AK
{
void * AllocHook( size_t in_size )
{
return malloc( in_size );
}
void FreeHook( void * in_ptr )
{
free( in_ptr );
}
}

AK::AllocHookAK::FreeHook は SDK 内で実装されていないが、SDK 内部にこれを呼ぶコードが存在する。おそらくプラットフォーム依存のコードを避けるためであろう。これらは各プラットフォームで有効な記述で実装してやる必要がある。


7. SDK の依存ライブラリのリンク指定

Wwise SDK が依存しているライブラリ、フレームワークをリンクするよう設定する。

これがちょっと厄介で、網羅的なドキュメントが見当たらなかった為、実際にビルドしてみてリンク失敗したオブジェクトがどのフレームワークに含まれるかを調べて追加した。

Mac の場合は


  • CoreAudio.framework

  • AudioUnit.framework

  • AudioToolbox.framework


  • OpenAL.framework

のリンクが必要であることがわかった。


8. Wwise セットアップ用のラッパーの作成

uneco/WwiseCocos2dx

私が作成した Wwise セットアップ用のラッパーを上記 GitHub リポジトリにホストしてある。 Wwise.hWwise.cpp は、IntegrationDemo からセットアップに関する記述を抜粋し、クラス化したものである。

動きの詳細はソースを確認のこと。使い方は下記を参考に、エラーハンドリングやログ出力等は適宜修正を加えてほしい。

// エラー格納用

char error[1024];

// サウンドバンクがあるディレクトリへのパス
char soundBankPath[128] = "./";

// 1. 規定の設定を取得
AkMemSettings memSettings;
AkStreamMgrSettings stmSettings;
AkDeviceSettings deviceSettings;
AkInitSettings initSettings;
AkPlatformInitSettings platformInitSettings;
AkMusicSettings musicInit;

Wwise::Instance().GetDefaultSettings(memSettings, stmSettings, deviceSettings, initSettings, platformInitSettings, musicInit);

// 2. Wwise 初期化
if (! Wwise::Instance().Init( memSettings, stmSettings, deviceSettings, initSettings, platformInitSettings, musicInit, soundBankPath, error, sizeof(error) )) {
// CCLOG は Cocos2d-x のログ出力コマンド
CCLOG("%s", error);
abort();
}

// 3. SoundBank のロード
// [Project] -> [SoundBanks] -> [Use SoundBank names] が off の場合
auto preloadSoundBanks = { AK::BANKS::INIT, AK::BANKS::DEFAULT };
for (auto soundBank : preloadSoundBanks) {
if (AK::SoundEngine::LoadBank(soundBank, AK_DEFAULT_POOL_ID) != AK_Success) {
abort();
}
}

3. について、Wwise オーサリング側の [Project] -> [SoundBanks] -> [Use SoundBank names] が on のときは以下。

// 3. SoundBank のロード

// [Project] -> [SoundBanks] -> [Use SoundBank names] が on の場合
auto preloadSoundBanks = { "Init.bnk", "Default.bnk" };
for (auto soundBank : preloadSoundBanks) {
AkBankID bankID;
auto res = AK::SoundEngine::LoadBank(soundBank, AK_DEFAULT_POOL_ID, bankID);
if (res != AK_Success) {
abort();
}
}


9. 最後に (音を鳴らす)

音を鳴らすために必要な処理して、フレーム毎に呼ばれる処理の中で AK::SoundEngine::RenderAudio() を呼んでやる必要がある。

Cocos2d-x の場合であれば、ルートシーンに scheduleUpdate() した上で void update(float delta) 内で実行してやればよいだろう。この処理を行わないと、すべてのイベント送信がキューイングされた状態で止まるため、プログラム側にも Wwise プロファイラ側にも一切のエラーが表示されず、ただ音が鳴らなくなる。

音が鳴らない場合は、これをしっかり呼んでいるかどうかを確認しよう。

// 毎フレーム実行される処理

void update(float delta) {
...
AK::SoundEngine::RenderAudio(); // !!とても重要!!
}

あとはイベントを投げるだけ。基本的には AK::SoundEngine::PostEvent を使えば良い。

// 攻撃時に呼ばれる

void Character::attackedCallback(Attack attack)
{
AK::SoundEngine::PostEvent(AK::EVENTS::PLAY_ATTACK, this->getGameObjectID());
// または
// AK::SoundEngine::PostEvent("Play_Attack", this->getGameObjectID());
}

第一引数は AK::EVENTS::PLAY_ATTACK のような Wwise_IDs.h で定義されている ID でも良いし、"Play_Attack" のようなイベント名の文字列でもよい。


以上、 Wwise を導入するにあたって私がつまづいた部分を含め、書いてみました。

記事に間違いがあればご指摘ください。質問等ございましたら、答えられる範囲で答えます。