JUCEでADX2LEを使ってAUプラグインを作る

#はじめに

JUCEはマルチプラットフォームなオーディオアプリを簡単に作れる環境ですごい。
プラグインもアプリケーションも同時に作れる。

プラグイン作成初心者ですが、JUCEのサンプルを参考にしつつ試して見ました。

#作った（作ろうと目指した）もの
JCUEを使って
ADX2LEのACFやACBといった、ゲームで使う目的のサウンドデータをサンプラーのように再生する
アプリケーションとAUプラグインを作ってみようと思いました。

#ADX2LEとは？
- ゲームエンジンやゲーム向けのサウンド再生モジュール。
- シンセ、ミキサー、エフェクタ、同時発音時の排他処理、ランダマイズなどゲーム向け機能が充実したサウンドライブラリ。
- 独自の圧縮コーデックを持っている。
- 大量の音声データやデータ読み込み中でも音途切れしないストリーミング再生などが簡単に行える。
- 無料。
リンク->[ADX2LE>https://game.criware.jp/products/adx2-le/]

#対象者
- ADX2LEをAUプラグインやC++で扱ってみたい人。
- MacでC++でADX2LEをつかってみたい人。
- ADX2LEのような外部スタティックライブラリをJUCEで使う場合の参考にしたい人。
- XcodeやJUCEは少し触ったことがある人。

ソースも少し載せていますが、参考になるか微妙なところ申しわけありません。
要望があれば公開できるような形にできればとも考えていますが、
何かしらのヒントになれば幸い。（かなり、わかる人向けのマニアックな内容です・・・）

#ADX2LEをMacで使う
ADX2LEのMac版は、Cocos2dxの中にMACOSX版のADX2LEがあります。
.aのstaticなライブラリと、includeのヘッダーを利用することで、Macで音を鳴らすことができます。

さらにacf　acbなどいろいろ必要。
C++で書く必要。

そこで、これGitHubで公開している　[MMLでDoremi](https://github.com/tatmos/ADX2LEMML20140119)
を移植してみようと思います。

# JUCEのProjucerを起動して
とりあえずAudioPlug-in形式ので作成。XCode用で。

# とりあえずソースを入れる

ADX2LEからヘッダー類
![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/0f44faf8-c626-531a-6f88-b3e5a4caaafe.png)

githubからSoundManager
https://github.com/tatmos/ADX2LEMML20140119/blob/master/Doremi/SoundManager.cpp


パスで問題になりそうなので、全てSourceの下に展開しています。
Publicにacf acbがあります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/d009dc6b-2e84-e7e6-53a7-fd3f42260159.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/df20dbb2-0cf8-5a4e-6645-32992e850c3f.png)

## XCodeでビルド

とりあえず　StandardPluginで

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/e4914686-eca1-afcb-c62d-a2cabbc84b78.png)


##エラーを解決していく

```
#include <cri_le_xpt.h>
```
だとうまくいかなかったので

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/8983171c-6604-e47b-3969-e3704e20e1f9.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/80388d1d-8ec1-2d66-b769-2f52854c5654.png)
いくつかある

```
#include "cri_le_xpt.h"
#include "cri_le_atom_ex.h"
#include "cri_le_atom_macosx.h"
#include "cri_le_atom_ex_monitor.h"
```
にそれぞれ変更。

##SoundManagerをMac版に書き換える
もともと、MMLを再生するものだったのですが、ソースは残したまま、MIDIキーで音程が変化するように再生できるものに初期化まわりなど変更

``` SoundManager.h
/*
  ==============================================================================

    SoundManager.h
    Created: 17 Nov 2017 7:32:50pm
    Author:  Takashi Tanaka

  ==============================================================================
*/

#pragma once
#include "cri_adx2le.h"
#include <iostream>
#include <string>
#include <iterator>
#include <vector>
#include <cctype>

using namespace std;

class SoundManager
{
public:
	SoundManager();
	~SoundManager();

	void Play(string &mml);
	
	void PlayNote(int noteNo,int velocity);
    void StopNote(int noteNo);

private:

	/* エラーコールバック関数 */
	static void user_error_callback_func(const CriChar8 *errid, CriUint32 p1, CriUint32 p2, CriUint32 *parray)
	{
		const CriChar8 *errmsg;

		/* エラー文字列の表示 */
		errmsg = criErr_ConvertIdToMessage(errid, p1, p2);
        //print("%s\n", errmsg);
        cout << errmsg << "\n";

		return;
	}

	static void *user_alloc_func(void *obj, CriUint32 size)
	{
		void *ptr;
		ptr = malloc(size);
		return ptr;
	}

	static void user_free_func(void *obj, void *ptr)
	{
		free(ptr);
	}

private:
	CriAtomExPlayerHn		player;		/* 再生プレーヤ */
	CriAtomExVoicePoolHn	voice_pool;	/* ボイスプール */
	CriAtomExAcbHn			acb_hn;		/* ACBハンドル(音声データ) */
	CriAtomDbasId			dbas_id;	/* D-BASの作成*/

	CriAtomExPlaybackId		playback_id;	/* VoiceキューのプレイバックID(再生開始時に保持する) */

	int noteNo = 0;
	int octNo = 4;
	int lenNo = 4;
	int volNo = 15;
	int tempo = 120;
    
    int lastPlaybackIdListforNote[128]; //  128キーのplaybackID

};
```

CPP側では、初期化まわりをCocos2dxのを参考に変更。

``` SoundManager.cpp
/*
  ==============================================================================

    SoundManager.cpp
    Created: 17 Nov 2017 7:32:50pm
    Author:  Takashi Tanaka

  ==============================================================================
*/
#include "unistd.h"
#include "SoundManager.h"
/* ACF/ACBのマクロ定義ヘッダ */
#include "Public/CueSheet_0.h"
#include "Public/Doremi_acf.h"


/* サンプルで使用するファイル名 */
const CriChar8* acf_file_name = "Public/Doremi.acf";
#define ACB_FILE			"Public/CueSheet_0.acb"
#define AWB_FILE			"Public/CueSheet_0.awb"

/* 最大ボイス数を増やすための関連パラメータ */
#define MAX_VOICE			(24)
#define MAX_VIRTUAL_VOICE	(MAX_VOICE + 8)		/* バーチャルボイスは多め */
#define MAX_CRIFS_LOADER	(MAX_VOICE + 8)		/* 読み込み数も多めに */

/* 最大サンプリングレート（ピッチ変更含む） */
#define MAX_SAMPLING_RATE	(48000*4)


/* HCA-MXコーデックのサンプリングレート */
const CriSint32 sampling_rate_hcamx = 32000;

/* ボイスプールへの設定 */
const CriSint32    max_voice            = 8;        /* デフォルト設定でも8    */
const CriSint32    max_sampling_rate    = 48000*2;    /* ピッチ変更も加味        */


CriAtomExVoicePoolHn    g_standard_voice_pool;        /* ボイスプールハンドル(ADX/HCAコーデック用)     */
CriAtomExVoicePoolHn    g_hcamx_voice_pool;            /* ボイスプールハンドル(HCA-MXコーデック用)     */

SoundManager::SoundManager()
{
	/* エラーコールバック関数の登録 */
	criErr_SetCallback(user_error_callback_func);

	/* メモリアロケータの登録 */
	criAtomEx_SetUserAllocator(user_alloc_func, user_free_func, NULL);

	/* ライブラリの初期化（最大ボイス数変更） */
//    CriAtomExConfig lib_config;
    CriFsConfig fs_config;
    criFs_SetDefaultConfig(&fs_config);
    fs_config.num_loaders = 100+16;        /* ざっくり多め。ストリーミング最大数＋ACB等の読み込みが使用 */
    
    
    CriAtomExConfig_MACOSX atom_macosx_config;
    criAtomEx_SetDefaultConfig_MACOSX(&atom_macosx_config);
    atom_macosx_config.atom_ex.fs_config = &fs_config;
    atom_macosx_config.hca_mx.output_sampling_rate = MAX_SAMPLING_RATE;
    atom_macosx_config.atom_ex.max_virtual_voices = 108;
    criAtomEx_Initialize_MACOSX(&atom_macosx_config, NULL, 0);
    
    /* ボイスプール作成 */
    CriAtomExStandardVoicePoolConfig vp_config;
    /* ボイスプールの設定。まずはデフォルト設定にして、その上で必要な値へ書き換えていく */
    criAtomExVoicePool_SetDefaultConfigForStandardVoicePool(&vp_config);
    vp_config.num_voices                        = max_voice;
    //vp_config.player_config.streaming_flag        = CRI_TRUE;
    vp_config.player_config.max_sampling_rate    = max_sampling_rate;
    /* 上で作った設定オブジェクトを渡して、ボイスプールを作成 */
    g_standard_voice_pool = criAtomExVoicePool_AllocateStandardVoicePool(&vp_config, NULL, 0);
    
    /* HCA-MX再生用のボイスプール作成 */
    CriAtomExHcaMxVoicePoolConfig hcamx_config;
    criAtomExVoicePool_SetDefaultConfigForHcaMxVoicePool(&hcamx_config);
    hcamx_config.num_voices                            = max_voice;
    /* 一応ストリーミング再生可能で初期化しておく。メモリ再生のみなら不要。 */
    hcamx_config.player_config.streaming_flag        = CRI_TRUE;
    hcamx_config.player_config.max_sampling_rate    = sampling_rate_hcamx;
    g_hcamx_voice_pool = criAtomExVoicePool_AllocateHcaMxVoicePool(&hcamx_config, NULL, 0);

	/* D-Basの作成（最大ストリーム数はここで決まります） */
	dbas_id = criAtomDbas_Create(NULL, NULL, 0);

	/* ACFファイルの読み込みと登録 */
	//criAtomEx_RegisterAcfFile(NULL, ACF_FILE, NULL, 0);
    
    
    CriChar8 acf_path[256];
    sprintf(acf_path, "%s", acf_file_name);
    criAtomEx_RegisterAcfFile(NULL, "/Users/takashitanaka/Documents/JUCE/ADX2LESynth/Source/Public/Doremi.acf", NULL, 0);

	/* DSP設定のアタッチ */
	criAtomEx_AttachDspBusSetting("DspBusSetting_0", NULL, 0);

	/* ボイスプールの作成（最大ボイス数変更／最大ピッチ変更／ストリーム再生対応） */
	CriAtomExStandardVoicePoolConfig vpool_config;
	criAtomExVoicePool_SetDefaultConfigForStandardVoicePool(&vpool_config);
	vpool_config.num_voices = MAX_VOICE;
	vpool_config.player_config.max_sampling_rate = MAX_SAMPLING_RATE;
	vpool_config.player_config.streaming_flag = CRI_TRUE;
	voice_pool = criAtomExVoicePool_AllocateStandardVoicePool(&vpool_config, NULL, 0);

	/* ACBファイルを読み込み、ACBハンドルを作成 */
	acb_hn = criAtomExAcb_LoadAcbFile(NULL,  "/Users/takashitanaka/Documents/JUCE/ADX2LESynth/Source/Public/CueSheet_0.acb", NULL,
                                       "/Users/takashitanaka/Documents/JUCE/ADX2LESynth/Source/Public/CueSheet_0.awb", NULL, 0);

    /* Player作成にも設定は必要 */
    CriAtomExPlayerConfig pf_config;
    criAtomExPlayer_SetDefaultConfig(&pf_config);
    pf_config.max_path_strings    = 1;
    pf_config.max_path            = 256;
    player                    = criAtomExPlayer_Create(&pf_config, NULL, 0);

	criAtomExPlayer_SetCueId(player, acb_hn, 4);

}


SoundManager::~SoundManager()
{
	/* DSPのデタッチ */
	criAtomEx_DetachDspBusSetting();

	/* プレーヤハンドルの破棄 */
	criAtomExPlayer_Destroy(player);

	/* ボイスプールの破棄 */
	criAtomExVoicePool_Free(voice_pool);

	/* ACBハンドルの破棄 */
	criAtomExAcb_Release(acb_hn);

	/* ACFの登録解除 */
	criAtomEx_UnregisterAcf();

	/* D-BASの破棄 */
	criAtomDbas_Destroy(dbas_id);

	/* ライブラリの終了 */
	criAtomEx_Finalize_MACOSX();
}

enum MMLType{
	none,
	note,
	len,
	rest,
	and_,
	tempo,
	period,
	octave,
	neiro,
	volume,
};

class Note
{
public:
	int lenNo = 0;
	int number = 0;
	MMLType mmlType = MMLType::none;
	int noteNo = 0;
	int octNo = 4;
	int volNo = 15;
};

bool IsInteger(const string &str)
{
	if (str.find_first_not_of("0123456789") != string::npos) {
		return false;
	}

	return true;
}


void SoundManager::Play(string &mml)
{
	string::iterator it = mml.begin();

	vector<Note> noteList;


	Note* newNote = NULL;
	MMLType mmlType = MMLType::none;



	while (it != mml.end())
	{
		string numberStr = "";
		int number = 0;

		string tmp;
		std::copy(it, it + 1, std::inserter(tmp, tmp.begin()));

		bool noteFlag = false;

		if (IsInteger(tmp))
		{
			numberStr = "";
			while (it != mml.end())
			{
				numberStr += tmp;
				it++;
				if (it == mml.end())break;
				tmp = "";
				std::copy(it, it + 1, std::inserter(tmp, tmp.begin()));
				if (IsInteger(tmp) == false){ it--; break; }
			}
			number = stoi(numberStr);

			switch (mmlType)
			{
			case MMLType::note:
			case MMLType::rest:
				if (newNote != NULL){
					newNote->lenNo = number;
				}break;
			case MMLType::len:lenNo = number; break;
			case MMLType::octave:octNo = number; break;
			case MMLType::volume:
				if (newNote != NULL){
					newNote->volNo = number;
					volNo = number;
				}break;
			case MMLType::tempo:
				if (newNote != NULL){
					newNote->number = number;
				}break;
			default:
				if (newNote != NULL){
					newNote->number = number;
				}break;
			}

		}
		else if (mblen(&((tmp.c_str())[0]), MB_CUR_MAX) != 1)
		{
			//cout << "!!" << tmp << endl;
			if (it == mml.end())break;
			it++;
		}
		else {

			if (tmp == "c"){ noteNo = 0; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "d"){ noteNo = 2; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "e"){ noteNo = 4; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "f"){ noteNo = 5; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "g"){ noteNo = 7; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "a"){ noteNo = 9; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "b"){ noteNo = 11; noteFlag = true; mmlType = MMLType::note; }
			else if (tmp == "r"){ noteNo = -255; noteFlag = true; mmlType = MMLType::rest; }
			else if (tmp == "&" || tmp == "^"){ noteNo = -254; noteFlag = true; mmlType = MMLType::and_; }
			else if (tmp == "@"){ noteNo = -253; noteFlag = true; mmlType = MMLType::neiro; }
			else if (tmp == "o"){ noteNo = -252; noteFlag = true; mmlType = MMLType::octave; }
			else if (tmp == "l"){ noteNo = -251; noteFlag = true; mmlType = MMLType::len; }
			else if (tmp == "t"){ noteNo = -250; noteFlag = true; mmlType = MMLType::tempo; }
			else if (tmp == "."){ noteNo = -249; noteFlag = true; mmlType = MMLType::period; }
			else if (tmp == "+")noteNo++;
			else if (tmp == "-")noteNo--;
			else if (tmp == ">")octNo++;
			else if (tmp == "<")octNo--;
			else if (tmp == "v"){ noteNo = -248; noteFlag = true; mmlType = MMLType::volume; }
			else { mmlType = MMLType::none; }

			if (noteFlag){
				newNote = new Note();
				noteList.push_back(*newNote);
				newNote = &(noteList[noteList.size() - 1]);
				newNote->octNo = octNo;
				newNote->lenNo = lenNo;
				newNote->volNo = volNo;
				newNote->mmlType = mmlType;
			}
		}
		if (newNote != NULL){
			newNote->noteNo = noteNo;
		}

		if (it == mml.end())break;
		it++;
	}

	{
		bool andFlag = false;
		float deltaLength = 0;

		vector<Note>::iterator it = noteList.begin();
		while (it != noteList.end())
		{
			switch ((*it).mmlType)
			{
			case MMLType::rest:criAtomExPlayer_Stop(player); break;
			case MMLType::note:
			{
				if (andFlag == false){
					criAtomExPlayer_Stop(player);
				}

				float pitch = (float)((((*it).octNo - 4) * 12) + (*it).noteNo) * 100 + 300;
				cout << pitch << " ";
				criAtomExPlayer_SetPitch(player, pitch);
				if (andFlag == false){
					playback_id = criAtomExPlayer_Start(player);
				}
				else {
					criAtomExPlayer_UpdateAll(player);
					andFlag = false;
				}

				deltaLength = (float)((*it).lenNo);
			} break;
			case MMLType::neiro:criAtomExPlayer_SetCueId(player, acb_hn, (*it).number); deltaLength = 0; break;
			case MMLType::tempo:tempo = (*it).number; deltaLength = 0; break;
			case MMLType::octave:deltaLength = 0; break;
			case MMLType::period: deltaLength = (float)deltaLength * 2; break;
			case MMLType::and_:deltaLength = 0; andFlag = true; break;
			case MMLType::volume:{
									 criAtomExPlayer_SetVolume(player, ((float)(*it).volNo / 15));
									 deltaLength = 0;
			} break;
			default:deltaLength = 0; break;
			}

			if (deltaLength != 0 && tempo != 0){
				float sleepTime = (500 * ((float)120 / tempo)) * ((float)4 / deltaLength);
				cout << sleepTime << endl;
				sleep((float)sleepTime/1000.0);
			}
			else {
				sleep(0);
			}
			++it;
		}

		cout << "end" << endl;
		criAtomExPlayer_Stop(player);
	}
}


void SoundManager::PlayNote(int noteNo,int velocity)
{
    if(noteNo >= 72)
    {
        criAtomExPlayer_SetCueId(player, acb_hn, noteNo-72);
        return;
    }
    
    if(lastPlaybackIdListforNote[noteNo] != 0)
    {
        //  すでに鳴っていたら止める
        StopNote(noteNo);
    }
    
    {
        float pitch = (noteNo-60) * 100 + 300;
        criAtomExPlayer_SetPitch(player, pitch);
        criAtomExPlayer_SetVolume(player, (float)velocity/127.0);
        
        lastPlaybackIdListforNote[noteNo] = criAtomExPlayer_Start(player);
    }
    
}
void SoundManager::StopNote(int noteNo)
{
    if(lastPlaybackIdListforNote[noteNo] != 0)
    {
        criAtomExPlayback_Stop(lastPlaybackIdListforNote[noteNo]);
        lastPlaybackIdListforNote[noteNo] = 0;
    }
    //criAtomExPlayer_Stop(player);
}

```

ACFやACBのパスも　とりあえずフルパス指定で。

音を止めるとかのためにplayback系のAPIを使ってたり。


![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/43fdc839-da06-5642-fe01-5b20ed592427.png)

## pianoの音がなるように

ヘッダーインクルードして
publicなところにsmを用意

``` PluginProcesser.h
#include "SoundManager.h"

...

public:
    SoundManager* sm;
```

コンストラクタの
    setSize (400, 300);の下あたりに

``` PluginEditor.cpp

    processor.sm = new SoundManager();    
    processor.sm->PlayNote(62,127);
```

て初期化と再生を追加。

##ライブラリの登録
![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/ca023bf8-3b32-eb95-85c5-58e3b82ac1e1.png)

Extra Linker flagsに設定

```
-lcri_ware_macosx_le
```

##外部ライブラリの検索パスを設定

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/be909022-2147-60f6-2850-70af70d3b466.png)

とりあえずフルパスでlibcri_ware_macosx_le.aの場所を指定。

これで、XCode上でstaticライブラリ（ADX2LE）を参照してビルドできる。





## スタティックライブラリのリンクとかうまくいかない場合

もし
スタティックライブラリのリンクとかうまくいかない場合

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/a35bdb5c-e0a7-fa39-d278-f98218f4c5a2.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/ea50abd3-20a4-90e8-f02a-5cb7eb048fd5.png)

XCode上でlibcri_ware_macosx_le.aを追加してみたところ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/f3f8bbad-b932-a843-c601-16fb40aca306.png)

Library Search PathをXCode上で指定してみたところ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/47a69c23-dc26-5f81-99ce-c558407058da.png)

Header Search PathをXCode上で指定してみたところ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/a7e6ba85-4545-5772-e1f1-1f950516b7cd.png)

Build Phases からlibcri_ware_macosx_leを設定してみたところ

で、cleanしてビルド

すると大抵動きます。


JUCEで設定変えて作り直すと消えちゃうので、JUCE側で設定しておくのがおすすめですが、とりあえず動かすなら上の方法でも可。



ライブラリパスはフルパスで書いてあるので、適宜自分の環境のパスに置き換えてください。




##MIDIの設定、ベンダーの設定

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/37d6c792-af7f-04ff-9d70-fbb714e7144c.png)

SynthでMidiInputを受け付けるように。
あと、yourcompanyのところを適当に変更する。

#Logicで音がなる。

LogicのMIDIで音がなるけれど、これはプラグインが一つのゲームのような状態で鳴っていて、
Logicのエフェクトをかけることができない。（同様にバウンスもできない）

さぁ　どうしようか・・・

##SoundManagerの初期化

``` PluginEditor.cpp
/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"


//==============================================================================
Adx2lesynthAudioProcessorEditor::Adx2lesynthAudioProcessorEditor (Adx2lesynthAudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p)
{
    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.
    setSize (400, 300);
    
    processor.sm = new SoundManager();
    
    //string str = u8"l4cderl8gec4d2";
    
    //processor.sm->Play(str);

    processor.sm->PlayNote(62,127);
}

Adx2lesynthAudioProcessorEditor::~Adx2lesynthAudioProcessorEditor()
{
}

//==============================================================================
void Adx2lesynthAudioProcessorEditor::paint (Graphics& g)
{
    // (Our component is opaque, so we must completely fill the background with a solid colour)
    g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));

    g.setColour (Colours::white);
    g.setFont (15.0f);
    g.drawFittedText ("Hello ADX2LE World!", getLocalBounds(), Justification::centred, 1);
}

void Adx2lesynthAudioProcessorEditor::resized()
{
    // This is generally where you'll want to lay out the positions of any
    // subcomponents in your editor..
}

```

###SoundManagerの宣言

``` PluginProcesspr.h
/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

#include "SoundManager.h"

class NoteObject
{
public:
    int noteNo = 0;
    int velocity = 0;
};


//==============================================================================
/**
*/
class Adx2lesynthAudioProcessor  : public AudioProcessor
{
public:
    //==============================================================================
    Adx2lesynthAudioProcessor();
    ~Adx2lesynthAudioProcessor();

    //==============================================================================
    void prepareToPlay (double sampleRate, int samplesPerBlock) override;
    void releaseResources() override;

   #ifndef JucePlugin_PreferredChannelConfigurations
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
   #endif

    void processBlock (AudioSampleBuffer&, MidiBuffer&) override;

    //==============================================================================
    AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override;

    //==============================================================================
    const String getName() const override;

    bool acceptsMidi() const override;
    bool producesMidi() const override;
    bool isMidiEffect () const override;
    double getTailLengthSeconds() const override;

    //==============================================================================
    int getNumPrograms() override;
    int getCurrentProgram() override;
    void setCurrentProgram (int index) override;
    const String getProgramName (int index) override;
    void changeProgramName (int index, const String& newName) override;

    //==============================================================================
    void getStateInformation (MemoryBlock& destData) override;
    void setStateInformation (const void* data, int sizeInBytes) override;

    
    SoundManager* sm;
private:
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Adx2lesynthAudioProcessor)
    NoteObject noteObjectList[16];
};

```

``` PluginProcessor.cpp
/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"


//==============================================================================
Adx2lesynthAudioProcessor::Adx2lesynthAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
                     #endif
                       )
#endif
{
}

Adx2lesynthAudioProcessor::~Adx2lesynthAudioProcessor()
{
}

//==============================================================================
const String Adx2lesynthAudioProcessor::getName() const
{
    return JucePlugin_Name;
}

bool Adx2lesynthAudioProcessor::acceptsMidi() const
{
   #if JucePlugin_WantsMidiInput
    return true;
   #else
    return false;
   #endif
}

bool Adx2lesynthAudioProcessor::producesMidi() const
{
   #if JucePlugin_ProducesMidiOutput
    return true;
   #else
    return false;
   #endif
}

bool Adx2lesynthAudioProcessor::isMidiEffect() const
{
   #if JucePlugin_IsMidiEffect
    return true;
   #else
    return false;
   #endif
}

double Adx2lesynthAudioProcessor::getTailLengthSeconds() const
{
    return 0.0;
}

int Adx2lesynthAudioProcessor::getNumPrograms()
{
    return 1;   // NB: some hosts don't cope very well if you tell them there are 0 programs,
                // so this should be at least 1, even if you're not really implementing programs.
}

int Adx2lesynthAudioProcessor::getCurrentProgram()
{
    return 0;
}

void Adx2lesynthAudioProcessor::setCurrentProgram (int index)
{
}

const String Adx2lesynthAudioProcessor::getProgramName (int index)
{
    return {};
}

void Adx2lesynthAudioProcessor::changeProgramName (int index, const String& newName)
{
}

//==============================================================================
void Adx2lesynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
}

void Adx2lesynthAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}

#ifndef JucePlugin_PreferredChannelConfigurations
bool Adx2lesynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
  #if JucePlugin_IsMidiEffect
    ignoreUnused (layouts);
    return true;
  #else
    // This is the place where you check if the layout is supported.
    // In this template code we only support mono or stereo.
    if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono()
     && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
        return false;

    // This checks if the input layout matches the output layout
   #if ! JucePlugin_IsSynth
    if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
        return false;
   #endif

    return true;
  #endif
}
#endif

void Adx2lesynthAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    const int totalNumInputChannels  = getTotalNumInputChannels();
    const int totalNumOutputChannels = getTotalNumOutputChannels();
    

    
    MidiBuffer::Iterator MidiItr(midiMessages);
    MidiMessage MidiMsg;int smpPos;
    while(MidiItr.getNextEvent(MidiMsg,smpPos))
    {
        if(MidiMsg.isNoteOn())
        {
            for(int currentTimberNo = 0;currentTimberNo < 16;currentTimberNo ++)
            {
                if(noteObjectList[currentTimberNo].velocity == 0)
                {
                    noteObjectList[currentTimberNo].noteNo = MidiMsg.getNoteNumber();
                    noteObjectList[currentTimberNo].velocity = 127;
                    
                    
                    sm->PlayNote(noteObjectList[currentTimberNo].noteNo,
                    noteObjectList[currentTimberNo].velocity);
                    
                    break;
                }
            }
            
        }
        if(MidiMsg.isNoteOff())
        {
            for(int currentTimberNo = 0;currentTimberNo < 16;currentTimberNo ++)
            {
                if(noteObjectList[currentTimberNo].velocity != 0 &&
                   noteObjectList[currentTimberNo].noteNo == MidiMsg.getNoteNumber()
                   )
                {
                    noteObjectList[currentTimberNo].velocity = 0;
                    
                    sm->StopNote(noteObjectList[currentTimberNo].noteNo);
                }
            }
        }
    }

    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        float* channelData = buffer.getWritePointer (channel);

        // ..do something to the data...
        
        //  ADX2LEのオーディオフィルターコールバックからbufferを得る
        
        //  bufferをコピーしておく
        
    }
}

//==============================================================================
bool Adx2lesynthAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

AudioProcessorEditor* Adx2lesynthAudioProcessor::createEditor()
{
    return new Adx2lesynthAudioProcessorEditor (*this);
}

//==============================================================================
void Adx2lesynthAudioProcessor::getStateInformation (MemoryBlock& destData)
{
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
}

void Adx2lesynthAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
}

//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new Adx2lesynthAudioProcessor();
}

```



# ADX2LEを再生エンジンとして使う
## メリット

・サンプラー的に使う時に、マルチボイス再生が可能なので、プログラム側はとてもシンプルに書ける。
・発音リミット処理とか、発音プライオリティ処理がデータ側に持っている。（変わりに　別途るCRI Atom Craftで設定が必要）
・内臓エフェクトでコンプやリミッターなどが使える。
・ゲーム向けなので、ランダムな音の再生が楽。（ランダムの制御は・・・Randomシードとか指定すればある程度固定化もできるかな）
・ゲーム向きなので処理が軽い。

## デメリット

- 複数同時使用などは想定外なので、同じアプリでプラグインとして動作させることが難しい。初期化とか工夫が必要そう。（すでに起動済みならとか動作を変更するなど）。
- ACFが異なる場合に動作が変わってしまう。
- もともとゲーム内でACFという共通データが１つしかロードが許されない構造になっている。（同じ　ACFで複数ACBはOK）

# おわりに
試しに無茶なプラグインが作れる環境として、JUCEは便利かもしれない。
もっとUIとか何か目標をたてて作っていくとよいかもしれない。

# ToDo

ネタ候補
・キューの一覧を表示して、そこから再生するキューを選ぶUIを作る。
・ACF,ACBの差し替えUIを作る。
・キューのInstモードとDrumモードを切り替える。（InstモードはNoteで音程が変わる。DrumモードはNoteでキューが変わる。）
・ModurationやMIDIでAISACやブロックインデクス、セレクタラベルのコントロール。
・ボリュームコントロール。
・PitchBendでピッチ変更
・Editボタンで、CRI Atom Craftの起動。


続き →
[JUCEでADX2LEをシングルスレッドで動かしたい](https://qiita.com/tatmos/items/ad0af8f5229e6e56274b)