hyoromoのブログ

最近はVRSNS向けに作ったものについて書いています

Cocos2dxで Cricket Audio を使ってみる

f:id:hyoromo:20150812035939p:plain
Cocos2dxフレームワークの SimpleAudioEngine や AudioEngine に機能不足やバージョンによってバグがあるため、CRIWAREやCricketを使っている企業が多いかと思います。今回は手が出しやすいCricketについて書きます。
http://www.crickettechnology.com/

今回試した環境は以下になります。

  • Mac OSX 10.4
  • Cocos2d-x v3.7
  • Cricket Audio version 1.5.0

国内での利用事例

国内のcocos2dx制アプリにて、いくつかの利用事例が発表されています。

  • 消滅都市
  • LINEタワーライジング
  • Braindots

他も確認したい場合はこちらから閲覧出来ます。

Criket Audioの特徴

Cricket Audioには2種類の再生方法があり、それぞれ以下のようになっています。

  • Bank sounds
    オンメモリ再生を行う形式。対応ファイル形式はwavもしくはaiffファイルのみ
  • Stream sounds
    ストリーミング再生を行う形式。対応ファイル形式はwav/mp3/mp4/m4a*1

Bank soundsはSE、Stream soundsはBGM再生の用途で使うことになります。

それ以外の特徴としては、以下になります。

  • ファイルを独自形式に変換(サウンドデータを抜かれて利用され難くなります)
  • 変換フォーマットは pcm8/PCM16/ADPCM から選択可能
  • panの指定*2
  • ループの開始と終了地点の指定
  • ループ回数の指定
  • ミキサーを使ったボリューム管理が行える
  • サウンド再生速度を調整出来る
  • 様々なエフェクトを付ける事が出来る

利用するために掛かる費用

ライセンス表記をアプリ上のどこかしらに表記し、Cricketへの利用報告する事で無料にて利用可能なようです。サポートを受けるには$99支払う必要があります。
もしクレジット表記やCriketへの利用報告(公式サイトへ利用アプリ掲載したくない)場合、ライセンス料を支払う事で免除されます。
http://www.crickettechnology.com/free_license

SDKのダウンロード

公式サイトのDownloadページから行えます。なお、DLにはアカウント作成が必要です。
http://www.crickettechnology.com/

DLファイル内の以下にドキュメントがあります。この後の説明で度々登場するので存在を覚えておいてください。
cricket-1.5.0/doc/Cricket.html

Bank soundsファイルの作成

オンメモリ上で再生するサウンドは .ckb ファイルとしてまとめての利用となります。

.ckbファイル作成用の設定ファイルの作成

利用対象の音声ファイルを設定ファイルに記述します。
サウンドファイルと同じディレクトリにて、.ckbxファイル(本エントリーでは samplesounds.ckbx)を以下のようにXML形式で作成します。

<?xml version="1.0" encoding="utf-8" ?>
<bank name="samplesounds">
    <sound name="se_click" source="se_click.wav" />
    <sound name="se_cancel" source="se_cancel.wav" />
</bank>

bank name はファイル名と合わせると分り易くなります。sound name に1ファイルずつ記載する必要があります。
1つの要素に対し、属性に「変換フォーマット/pan/ループ回数/ループ開始・終了位置」等が設定可能です。詳しくは以下の「Sound attributes」項目を参照ください。
cricket-1.5.0/doc/manual/cktool.html

.ckbファイル作成

cricket-1.5.0/bin/macosx/cktool を適当な場所*3へ配置し、先ほど作成した .ckbx ファイルが存在するディレクトリにて以下コマンドを実行します。

$ cktool buildbank samplesounds.ckbx

成功すると samplesounds.ckb ファイルが作成されており、こちらをiOS/AndroidそれぞれのIDEに追加して利用する事になります。

Stream soundsファイルの作成

ストリーミング再生するサウンドは1ファイルずつ独自形式に出力して使用するか、前述したmp3等のエンコード済みファイルをそのまま利用します。
独自形式に変換したい場合は以下のようにします。

$ cktool buildstream -format adpcm bgm_stage1.wav bgm_stage1.cks

iOSの環境設定

Xcodeへのファイル追加

以下をプロジェクトの任意ディレクトリへコピーし、Xcode上で追加しておきます。

  • cricket-1.5.0/inc/ck
  • cricket-1.5.0/lib/ios/Release/libck.a

TARGETS-Build Settingsの「Header Search Paths」に inc フォルダへのパスを追加し、「Library Search Paths」に lib フォルダへのパスを追加します。

framework追加

追加するframeworkは以下になります。

  • CoreMedia.framework
  • AVFoundation.framework
  • MediaPlayer.framework

Androidの環境設定

Android.mkを以下のようにします。

LOCAL_PATH := $(call my-dir)

#-------------------------------------------------------------
# specify libck as a "prebuilt" static library

include $(CLEAR_VARS)
LOCAL_MODULE := ck

ifeq ($(NDK_DEBUG),1)
LOCAL_SRC_FILES := ../lib/cricker/$(TARGET_ARCH_ABI)/debug/libck.a
else
LOCAL_SRC_FILES := ../lib/cricker/$(TARGET_ARCH_ABI)/release/libck.a
endif

include $(PREBUILT_STATIC_LIBRARY)
    
#-------------------------------------------------------------

include $(CLEAR_VARS)

$(call import-add-path,$(LOCAL_PATH)/../../cocos2d)
$(call import-add-path,$(LOCAL_PATH)/../../cocos2d/external)
$(call import-add-path,$(LOCAL_PATH)/../../cocos2d/cocos)

LOCAL_MODULE += cocos2dcpp_shared

LOCAL_MODULE_FILENAME := libcocos2dcpp

LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../Classes/AppDelegate.cpp \
                   ../../Classes/HelloWorldScene.cpp \
                   ../../Classes/CricketAndroidJni.cpp

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../Classes/lib

# _COCOS_HEADER_ANDROID_BEGIN
# _COCOS_HEADER_ANDROID_END


LOCAL_STATIC_LIBRARIES := cocos2dx_static
LOCAL_STATIC_LIBRARIES += ck cpufeatures

# _COCOS_LIB_ANDROID_BEGIN
# _COCOS_LIB_ANDROID_END

LOCAL_LDLIBS     += -llog -landroid

include $(BUILD_SHARED_LIBRARY)

$(call import-module,.)
$(call import-module,android/cpufeatures)

# _COCOS_LIB_IMPORT_ANDROID_BEGIN
# _COCOS_LIB_IMPORT_ANDROID_END

Cocos2dx v3.7 で新規作成した状態に、以下に記載されている内容を書いたものになっています。
cricket-1.5.0/doc/manual/building.html#building_for_android_ndk

C++での実装

公式のサンプルコードほぼそのままですが、以下のように実装します。

BGM/SEをそのまま流すだけの方法

見やすいようにグローバル変数にしていませんが、適当に読み替えてください。

#include "ck/ck.h"
#include "ck/config.h"
#include "ck/bank.h"
#include "ck/sound.h"

CkBank* _bank;
CkSound* _soundEffect;
CkSound* _music;

///////////////// 中略 /////////////////

/* 再生 */
#if CK_PLATFORM_ANDROID
    // Androidでの初期化はAppActivity.javaにて行っている
#else
    CkConfig config;
    CkInit(&config);
#endif

// Bank sounds ファイルの再生
_bank = CkBank::newBank("samplesounds.ckb");
_soundEffect = CkSound::newBankSound(_bank, "se_click");    // 別関数にてckbxで設定したindex番号でも呼び出し可能
_soundEffect->play();

// Stream sounds ファイルの再生
_music = CkSound::newStreamSound("bgm_stage1.cks");
_music->setLoopCount(-1);
_music->play();

///////////////// 中略 /////////////////

void MainScene::update(float delta)
{
    CkUpdate();    // Stream sounds再生を行うのに必須関数
}

///////////////// 中略 /////////////////

/* 解放 */
_soundEffect->destroy();
_music->destroy();
_bank->destroy();

CkShutdown();

Androidの初期化はContextが必要なのでAppActivity.javaに書きました。もしかしてら微妙な方法かもしれませんので、もっと良い方法があればコメントで教えて下さい。

public class AppActivity extends Cocos2dxActivity {

    private static native void initCricket(Context context);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AppActivity.initCricket(this.getApplicationContext());
    }
}
//  CricketAndroidJni.h
#include <jni.h>

#ifndef __ANDROID_JNI__
#define __ANDROID_JNI__
#ifdef __cplusplus
extern "C" {
#endif
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_initCricket(JNIEnv *, jobject obj, jobject thiz);
    
#ifdef __cplusplus
}
#endif
#endif __ANDROID_JNI__
//  CricketAndroidJni.cpp
#include "CricketAndroidJni.h"

#include "ck/ck.h"
#include "ck/config.h"

JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_initCricket(JNIEnv *env, jobject obj, jobject thiz) {
    CkConfig config(env, thiz);
    CkInit(&config);
}
サウンドのミックス処理

音声ファイル毎にグループを作り、特定条件下で指定グループの音量を調整する事が出来ます。
※前コードと重複する箇所は省略しています。

#include "ck/mixer.h"

CkMixer* _mixer;

///////////////// 中略 /////////////////

// bgmグループを作成
_mixer = CkMixer::newMixer("bgm");
_mixer->setParent(CkMixer::getMaster());
_mixer->setVolume(1.0f);

// 前コードで作成した_sound変数をbgmグループに所属させます
_sound->setMixer(_mixer);

///////////////// 中略 /////////////////

// bgmグループの音量を下げる(他効果音を目立たせる使用例)
_mixer->setVolume(0.2f);
auto seSound = CkSound::newBankSound(_bank, "se_fire");
seSound->play();

///////////////// 中略 /////////////////

// bgmグループの音量を何かしらのタイミングで戻します(今回は効果音再生完了後を契機とする)
void MainScene::update(float delta) {
    if (!seSound->isPlaying()) {
        _mixer->setVolume(1.0f);
    }
}

///////////////// 中略 /////////////////

/* 解放 */
_mixer->destroy();

CkSoundはsetMixerで何も設定していない場合、CkMixer::getMaster() に所属しています。MasterはMixerのrootであり、rootの下に作成したMixerをぶら下げて管理する事になります。
子Mixerは親Mixerの音量変更に対して影響する為、管理の際にはその点に注意が必要です。

サウンドのエフェクト処理

Bank/Stream Soundsに対してフィルターを掛ける事が出来ます。

CkEffect* _effect;

///////////////// 中略 /////////////////

// エフェクト
_effect = CkEffect::newEffect(kCkEffectType_BiquadFilter);
CkEffectBus* bus = CkEffectBus::newEffectBus();
bus->addEffect(_effect);
_music->setEffectBus(bus);

///////////////// 中略 /////////////////

/* 解放 */
_effect->destroy()

kCkEffectTypeには4種類あり、それぞれ以下のようになっています。

kCkEffectType 効果
kCkEffectType_BiquadFilter 音が篭って聞こえる
kCkEffectType_BitCrusher 8bitサウンドのように聞こえる(ノイズが入る)
kCkEffectType_RingMod リングモジュレーターを掛けます
kCkEffectType_Distortion ディストーションエフェクトを掛けます

iOSではエフェクトが掛かりましたが、何故かAndroidでは掛かりませんでした。

その他

知っておきたいその他のAPI。SuspendとResumeはアプリのライフサイクルに応じて AppDelegate.cpp に記載する必要があります*4

// サウンドのスピード調整。デフォルト1。1未満なら遅くなるし、1超なら速くなる
_music->setSpeed(2);

// 一時停止
CkSuspend();

// 再開
CkResume();

まとめ

独自形式による安心感や、音声品質の安定化、サウンドのミックス機能によるPGが管理しやすい仕組み等があり、無料で使えるなら申し分ないです。
が、iOS側のライブラリ ファイルサイズが 21.6 MBもあるのが導入を躊躇させるミドルウェアとなっています。ちなみにAndroid側は約7MBです。

*1:iOS/Androidの両方で再生可能なフォーマットのみ記載

*2:ステレオ再生時の左右から聞こえる音量バランス調整。-1.0〜+1.0での指定となる

*3:/usr/local/bin 等

*4:iOSは記載しなくてもプラットフォーム側で音声再生が停止させられますが、Androidは鳴りっぱなしになるため