はじめに
前回OpenJTalkForUnity
というUnityで簡単にOpenJTalk
を利用できるようにしたパッケージを紹介しました。
www.hanachiru-blog.com
ただこのパッケージはWindows
でしか動作しないという欠点があり、どうしてもクロスプラットフォームで動作させたかったので自作C#ラッパーを作成しようと思っていたのですが、SharpOpenJTalk
なるものを発見しました。
github.com
実際にどれくらいのプラットフォームで動作するかは検証する必要がありますが、ひとまず動かすところまでやってみようと思います。
環境
Unity2020.3.5f1
SharpOpenJTalk v1.4.0
VisualStudioのNuGetからインポート&ビルド
NuGetForUnity
というUnity用のNuGet
クライアントがあるのですが、経験上うまくいったことが少ないのでVisual Studio
のNuGet
から取ってきてUnityに入れます。
github.com
Visual Studio
を立ち上げ、TargetPlatform
を.NET Framework4.72
のコンソールアプリケーションを作成します。
// OpenJTalkSample.csproj <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net472</TargetFramework> <Platforms>AnyCPU;x64;x86</Platforms> </PropertyGroup> <ItemGroup> <PackageReference Include="SharpOpenJTalk" Version="1.4.0" /> </ItemGroup> </Project>
あとはNuGet
よりSharpOpenJTalk
をインポートし、ビルドを行います。
一つ注意点なのですが、x64
とx86
で別々でビルドしないと私の場合はUnity側でエラーを吐いてしまいました。
以下の3つのdll
をUnityのPlugins
フォルダに入れます。
- openjtalk
- SharpOpenJTalk
- System.Diagnostics.DiagnosticSource
またx64
とx86
の2種類あると思うので、以下の画像のように配置・設定を行ってください。
サンプルを実行してみる
公式サンプルを参考にしながら、StreamingAssets
内に以下の2つのデータを配置した場合のサンプルを記述します。(各々の手法で以下の2つのファイルを取得してください)
- 辞書
- 音響モデル
SharpOpenJTalk/Program.cs at master · yamachu/SharpOpenJTalk · GitHub
using System; using System.IO; using UnityEngine; using SharpOpenJTalk; public class Sample : MonoBehaviour { private void Start() { var instance = new OpenJTalkAPI(); var dictPath = Path.Combine(Application.streamingAssetsPath, "dic_utf_8"); var hmmModelPath = Path.Combine(Application.streamingAssetsPath, "voice/hts_voice_nitech_jp_atr503_m001-1.05/nitech_jp_atr503_m001.htsvoice"); if (!instance.Initialize(dictPath, hmmModelPath)) { Debug.LogError("Initialize failed"); return; } var labels = instance.GetLabels("これはテストです"); foreach (var label in labels) { Debug.Log(label); } var buffer = instance.SynthesisBuffer("これはテストです"); if (buffer == null) { Debug.LogError("Synthesis failed"); return; } Debug.Log(buffer.Length); SaveToFile(buffer, "Assets/test.wav"); } private static void SaveToFile(short[] buffer, string path) { var byteWidth = 16 / 8; var freq = 48000; using (var fs = new FileStream(path, FileMode.OpenOrCreate)) using (var bw = new BinaryWriter(fs)) { bw.Write("RIFF".ToCharArray()); bw.Write((UInt32)(36 + buffer.Length * byteWidth)); bw.Write("WAVE".ToCharArray()); bw.Write("fmt ".ToCharArray()); bw.Write((UInt32)16); bw.Write((UInt16)1); bw.Write((UInt16)1); bw.Write((UInt32)freq); bw.Write((UInt32)(freq * byteWidth)); bw.Write((UInt16)(byteWidth)); bw.Write((UInt16)16); bw.Write("data".ToCharArray()); bw.Write((UInt32)(buffer.Length * byteWidth)); var maxVal = (short)32767; var minVal = (short)-32768; foreach (var v in buffer) { if (v > maxVal) bw.Write(maxVal); else if (v < minVal) bw.Write(minVal); else bw.Write((short)v); } } } }
無事に実行ができると、Assets
内にtest.wav
が出力されているはずです。