FMODを使う【Windows】【波形再生】


# 前提
* 環境
    + Windows7
    + Visual Studio Community ( Visual Studio 2015 )
    + fmod 1.08.05
    + Cocos2d-x 3.10 (あまり関係ないです)
        - [Cocos 3.10](http://www.cocos2d-x.org/download)インストール。
        - Engine TypeをSource Codeでプロジェクト作成
* 自分にサウンドの知識はないです。知りたいです。

# 目的
* 波形を再生する
* ついでに波形のスペクトラム情報も取得してみる
    + 正直、データとして正しいかはよくわからず

# 準備
1. fmodの会員登録する
    * ライセンスとしては、インディー・開発費が10000$以下・年一本なら無料でつかえるとのこと
    * http://www.fmod.org/profile/register/
1. FMOD Studio Programmer's API and Low Level Programmer API のWindows版をインストール
    * http://www.fmod.org/download/
1. ライブラリをプロジェクトへ追加
    * https://github.com/fmod/EarthWarrior3D を参考に。
    * ちゃんとパスが通せれば、この通りである必要はないと思います。
1. パスを通す
    * Cocos2d-x を使ってると、includeパスがcocos2d/externalに通ってるもよう
    * ライブラリパスは通ってないみたいなので、通す。
        + Project > Property > Linker > General > Additioanl Library Directory > $(EngineRoot)external\fmod\prebuild\win32を追加
        + Project > Property > Linker > Input > Additional Depencise > fmodL_vc.libを追加

# コード

* FmodWrapperLayer.h

```cpp
class FmodWrapperLayer : public cocos2d::Layer
{
public:
    FmodWrapperLayer();

    virtual bool init() override;

    virtual void update(float delta) override;

    CREATE_FUNC(FmodWrapperLayer);

private:
    FMOD::System	*system;
    FMOD::DSP		*dsp;
    FMOD::DSP		*spectrumDSP;
    FMOD::Channel	*channel;
};
```

* FmodWrapperLayer.cpp

```cpp  
#include "FmodWrapperLayer.h"
#include <fmod/include/fmod_studio.hpp>
#include <assert.h>

#deifne CHECK_RESULT(result) assert(result == FMOD_OK)

FmodWrapperLayer::FmodWrapperLayer()
: system( nullptr ) 
, dsp( nullptr )
, spectrumDSP( nullptr )
, channel( nullptr )
{ }

bool FmodWrapperLayer::init()
{
    if (!Layer::init())
    {
        return false;
    }

    {
        // fmodの初期化などなど
        FMOD_RESULT result = FMOD_RESULT::FMOD_OK;
        result = FMOD::System_Create(&system);
        CHECK_RESULT(result);
        result = system->init(32, FMOD_INIT_NORMAL | FMOD_INIT_PROFILE_ENABLE, NULL);
        CHECK_RESULT(result);

        result = system->createDSPByType(FMOD_DSP_TYPE_OSCILLATOR, &dsp);
        CHECK_RESULT(result);
        result = dsp->setParameterFloat(FMOD_DSP_OSCILLATOR_RATE, 440.0f);
        CHECK_RESULT(result);

        result = dsp->reset();
        CHECK_RESULT(result);
        result = system->playDSP(dsp, NULL, true, &channel);
        CHECK_RESULT(result);
        result = channel->setVolume(0.5f);
        CHECK_RESULT(result);
        // 第二引数は波形の形を指定。
        // /* (Type:int) - Waveform type.  0 = sine.  1 = square. 2 = sawup. 3 = sawdown. 4 = triangle. 5 = noise.  */
        result = dsp->setParameterInt(FMOD_DSP_OSCILLATOR_TYPE, 0);
        CHECK_RESULT(result);
        result = channel->setPaused(false);
        CHECK_RESULT(result);

        // spectrum情報用
        result = system->createDSPByType(FMOD_DSP_TYPE::FMOD_DSP_TYPE_FFT, &spectrumDSP);
        CHECK_RESULT(result);
        result = spectrumDSP->setParameterInt(FMOD_DSP_FFT_WINDOWSIZE, 512);
        CHECK_RESULT(result);
        result = spectrumDSP->setParameterInt(FMOD_DSP_FFT_WINDOWTYPE, FMOD_DSP_FFT_WINDOW_HAMMING);
        CHECK_RESULT(result);
        result = channel->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, spectrumDSP)
        CHECK_RESULT(result);
    }

    // Updateの利用を開始( cocos2d-x特有 )
    // 通常のシステムならこういうのはいらないはず
    scheduleUpdate();

    return true;
}

void FmodWrapperLayer::update(float delta)
{
    // spectrum表示
    if (spectrumDSP != NULL)
    {
        FMOD_RESULT result = FMOD_RESULT::FMOD_OK;

        unsigned int dataLength = 0;
        FMOD_DSP_PARAMETER_FFT* fftParam = nullptr;
        char str[1024] = {};
        result = spectrumDSP->getParameterData(FMOD_DSP_FFT_SPECTRUMDATA, (void**)&fftParam, &dataLength, str, 1024);
        CHECK_RESULT(result);

        for (int channels = 0; channels < fftParam->numchannels; channels++)
        {
            for (int bin = 0; bin < fftParam->length; bin++)
            {
                float val = fftParam->spectrum[channels][bin];
                if (val == 0.0f) { continue; }
                CCLOG("val = %f", val);
            }
        }
    }

    // fmodは定期的な更新が必要
    if (system != NULL)
    {
        FMOD_RESULT result = FMOD_RESULT::FMOD_OK;

        result = system->update();
    }

}
```