AquesTalk10をUnity（WindowsおよびAndroid）から呼び出す

音声合成ライブラリ「AquesTalk」をUnity（WindowsおよびAndroid）から呼び出してみました。

AquesTalk
https://www.a-quest.com/products/aquestalk.html

githubにソースをアップしてあります。
https://github.com/isdf/AquesTalk10UnitySample

#ライブラリを配置する
まずいい感じに置きます。

参考資料
UnityでDLLを使う
http://tvmllab.com/unity%E3%81%A7dll%E3%82%92%E4%BD%BF%E3%81%86/

Android 用ネイティブ（C++）プラグインの配置方法
https://docs.unity3d.com/jp/560/Manual/AndroidNativePlugins.html

.dllおよび.soを選択しInspectorで見た際に、
PlatforomSettingsに複数チェック（x86とx86_64の両方など）が入っているとビルドエラーになります。


#呼び出す

ポイント！

・再生用オーディオソース（public AudioSource audio_）にはSceneに配置した
　AudioSourceをHierarchy上で登録しておく。

・AQTK_VOICE構造体はアンマネージな所に渡すので以下のように順番やバイト数を整えておく。
[StructLayout(LayoutKind.Sequential, Pack = 4)]

・ライブラリ側でポインタを引数にしている場所には
    [DllImport("AquesTalk")]
    private static extern IntPtr AquesTalk_Synthe(ref AQTK_VOICE pParam, byte[] koe, ref int size);
　のようにrefで渡してあげたら動いた。


・~~AquesTalkはSJISの日本語ひらがな文字列を引数にして音声合成するため、~~
　~~Unityの標準であるUTF8は変換する必要がある。しかしSJISにはすんなりとは変換できない。~~

　~~ここらへんを参考にされたし。~~
　~~System.Text.Encoding で Shift JIS を使いたい~~
　~~https://helpdesk.unity3d.co.jp/hc/ja/articles/204694010-System-Text-Encoding-%E3%81%A7-Shift-JIS-%E3%82%92%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%84~~

追記
AquesTalk_Synthe_Utf8()を呼ぶことでUTF8でも問題なく渡せる模様。流石AquesTalk10。


・AquesTalkが返してくれるbyte配列を直接AudioClipで再生できる音声には変換できないので、
　float配列に変換する必要がある。

　こちらのテクを拝借。
　https://github.com/Suzeep/audioclip_maker


```ccharp:AquesTalk10SamplePlayer.cs
using UnityEngine;
using System;
using System.Runtime.InteropServices;

public class AquesTalk10SamplePlayer : MonoBehaviour
{

	//再生用オーディオソース
	public AudioSource audio_;

	// 声質パラメータ
	[StructLayout(LayoutKind.Sequential, Pack = 4)]
	struct AQTK_VOICE
	{
		public int bas;    // 基本素片 F1E/F2E/M1E (0/1/2)
		public int spd;    // 話速 	50-300 default:100
		public int vol;    // 音量 	0-300 default:100
		public int pit;    // 高さ 	20-200 default:基本素片に依存
		public int acc;    // アクセント 0-200 default:基本素片に依存
		public int lmd;    // 音程１ 	0-200 default:100
		public int fsc;    // 音程２(サンプリング周波数) 50-200 default:100
		public void Init()
		{
			bas = 0;    // 基本素片 F1E/F2E/M1E (0/1/2)
			spd = 100;    // 話速 	50-300 default:100
			vol = 100;    // 音量 	0-300 default:100
			pit = 100;    // 高さ 	20-200 default:基本素片に依存
			acc = 100;    // アクセント 0-200 default:基本素片に依存
			lmd = 100;    // 音程１ 	0-200 default:100
			fsc = 100;    // 音程２(サンプリング周波数) 50-200 default:100
		}
	}

	[DllImport("AquesTalk")]
	private static extern IntPtr AquesTalk_Synthe(ref AQTK_VOICE pParam, byte[] koe, ref int size);

	[DllImport("AquesTalk")]
	private static extern void AquesTalk_FreeWave(IntPtr wavPtr);


	//https://github.com/Suzeep/audioclip_makerよりbyte配列→再生用float配列への変換処理を拝借
	readonly float RANGE_VALUE_BIT_8 = 1.0f / Mathf.Pow(2, 7);   // 1 / 128
	readonly float RANGE_VALUE_BIT_16 = 1.0f / Mathf.Pow(2, 15); // 1 / 32768
	const int BASE_CONVERT_SAMPLES = 1024 * 20;
	const int BIT_8 = 8;
	const int BIT_16 = 16;

	//---------------------------------------------------------------------------
	// create rawdata( ranged 0.0 - 1.0 ) from binary wav data
	//---------------------------------------------------------------------------
	public float[] CreateRangedRawData(byte[] byte_data, int wav_buf_idx, int samples, int channels, int bit_per_sample)
	{
		float[] ranged_rawdata = new float[samples * channels];

		int step_byte = bit_per_sample / BIT_8;
		int now_idx = wav_buf_idx;

		for (int i = 0; i < (samples * channels); ++i)
		{
			ranged_rawdata[i] = convertByteToFloatData(byte_data, now_idx, bit_per_sample);

			now_idx += step_byte;
		}

		return ranged_rawdata;
	}

	//---------------------------------------------------------------------------
	// convert byte data to float data
	//---------------------------------------------------------------------------
	private float convertByteToFloatData(byte[] byte_data, int idx, int bit_per_sample)
	{
		float float_data = 0.0f;

		switch (bit_per_sample)
		{
			case BIT_8:
				{
					float_data = ((int)byte_data[idx] - 0x80) * RANGE_VALUE_BIT_8;
				}
				break;
			case BIT_16:
				{
					short sample_data = System.BitConverter.ToInt16(byte_data, idx);
					float_data = sample_data * RANGE_VALUE_BIT_16;
				}
				break;
		}

		return float_data;
	}

	public void Synthe()
	{
		Debug.Log("Synthe() start");

		// 不定長情報のメモリ確保
		IntPtr aqtk_p = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(AQTK_VOICE)));
		AQTK_VOICE aqtk_voice = (AQTK_VOICE)Marshal.PtrToStructure(aqtk_p, typeof(AQTK_VOICE));
		aqtk_voice.Init();

		//バイト配列をUTF8の文字コードとしてStringに変換する
		//string koeUtfStr = "ゆっくりしていってね";
		//System.Text.Encoding utf8Enc = System.Text.Encoding.GetEncoding("UTF-8");
		//System.Text.Encoding sjisEnc = System.Text.Encoding.GetEncoding("Shift_JIS");
		//byte[] koeUtfBytes = utf8Enc.GetBytes(koeUtfStr);
		//byte[] koeSjisBytes = System.Text.Encoding.Convert(utf8Enc, sjisEnc, koeUtfBytes);

		//https://helpdesk.unity3d.co.jp/hc/ja/articles/204694010-System-Text-Encoding-%E3%81%A7-Shift-JIS-%E3%82%92%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%84
		//System.Text.Encoding で Shift JIS を使いたい
		//高橋 啓治郎 - 2017年04月07日 17:08
		//Unity の Standalone Player は Shift_JIS (codepage 932) の encoding を含んでおらず、
		//これを System.Text.Encoding で使おうとするとエラーになります。

		//SJIS文字列（あいうえお）
		byte[] koeSjisBytes = { 0x82, 0xa0, 0x82, 0xa2, 0x82, 0xa4, 0x82, 0xa6, 0x82, 0xa8, };

		int size = 0;
		IntPtr wavPtr = AquesTalk_Synthe(ref aqtk_voice, koeSjisBytes, ref size);
		Debug.Log("size : " + size);

		//成功判定
		if (wavPtr == IntPtr.Zero)
		{
			Debug.LogError("ERROR: 音声生成に失敗しました。不正な文字が使われた可能性があります");
		}

		//C#で扱えるようにマネージド側へコピー
		byte[] byte_data = new byte[size];
		Marshal.Copy(wavPtr, byte_data, 0, size);

		//アンマネージドポインタは用が無くなった瞬間に解放
		AquesTalk_FreeWave(wavPtr);

		//float配列に変換
		float[] float_data = CreateRangedRawData(byte_data, 0, size / 2, 1, BIT_16);

		//audioClip作成
		AudioClip audioClip = AudioClip.Create("AquesTalk", float_data.Length, 1, 16000, false);
		audioClip.SetData(float_data, 0);
		audio_.clip = audioClip;

		//再生
		audio_.Play();

		Debug.Log("Synthe() end");
	}

}
```


iOSはうちのmacがアレしててテストできないのでどなたかお願いします。