読者です 読者をやめる 読者になる 読者になる

眠いしお腹すいたし(´・ω・`)

C#関連を主に書きます。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Xamarin.Androidでゆっくりボイスを喋らせてみた( *´艸`)

Android C# Xamarin

ごあいさつ

先週は頭に霧がかかったみたいにずっとモヤモヤしていたのと吐き気で物が食べられなくて大変でしたが、金曜日にとあるエンジニアの方々とお話しする機会がありまして色々と意見をもらえた事と、私自身が現状の何が嫌なのかがハッキリしてとてもスッキリしたことが相まって調子が回復いたしました。

ですのでおうちプログラムをしてみました。

きっかけ

この間Xamarin Dev Daysの課題アプリで音声読み上げがあったのですが、日本で音声読み上げといえば・・・

ゆっくりしていってね!!!

ゆっくりボイス

ゆっくりしていってね!!!

でおなじみゆっくりボイスは

dic.nicovideo.jp

softalkという音声読み上げソフトが読み上げる独特な音声です。

SoftalkAquesTalkというライブラリを使用して作られています。

このAquesTalk Android用ライブラリ、iOS用ライブラリがあるんです。

ということは~~

なんとかすればXamarinでも使えるのではないでしょうか?

ということで まずはAndroidでゆっくりボイスをしゃべらせてみましょう

ダウンロード

まずは評価版のライブラリをダウンロードしてきます。

http://www.a-quest.com/download/index.html

このAndroidの評価版は二つの制限があるそうです。

  1. 「ナ行、マ行」を指定すると、すべて「ヌ」と発声します
  2. armeabi用だけで、x86mips用のライブラリは含まれません

とくに二つ目の制限が結構きついです。

armeabi-v7aではありません。 armeabi用のsoファイルしか用意されていないのです。

v7aでは無いということはAndroid 2.3辺りということです。

AQUEST様、さすがにv7aじゃないarmeabiはつらいです。

今どきのエミュレーター事情を考えるとx86用を評価用に用意してもらえると嬉しいです。

zipファイルを展開すると

  • soファイル
  • NativeLibrary呼び出しクラスサンプル(.java)
  • サンプルアプリプロジェクト

が入っています。

今回はXamarin Androidで Samplesの中にあるAqTkAppというアプリを作ってみたいと思います。

サンプルプロジェクト

github.com

こちらのリポジトリに今回作成したアプリを載せました。

詳しくはこちらをご覧ください。

  • なお、こちらのリポジトリにはsoファイルは入っていません。*

作成

プロジェクト作成

Xamarin.Androidプロジェクトを普通に作ってください。

プロジェクトへSOファイルを追加する

NativeLibraryを使用するため

f:id:tamafuyou:20161121214850p:plain

このようなフォルダ構成でsoファイルをプロジェクトに追加します。

f:id:tamafuyou:20161121215012p:plain

追加したsoファイルのビルドアクションをAndroidNativeLibraryに変更します。

Layoutとstringsを作成する

f:id:tamafuyou:20161121215227p:plain

参考元のプロジェクトのmain.xmlファイルをそのままコピーしてきます。

strings.xmlもそのまんま丸写しで大丈夫です。

f:id:tamafuyou:20161121215650p:plain

こんな感じになるかと思います。

AquresTalkクラスを作成する

今回の核心部分です。これで3日も悩みました。

当初soファイルを追加したのでP/Invokeで直接soファイルの関数を呼び出せばよいのだろうと考えていたのですが、どうしてもうまくいきませんでした。

2日頑張ってNativeメソッドのコールまでは出来ていそうな感じになったのですがエラーでアプリが落ちてしまってどうしても原因がわかりませんでした・・・

ですので今回はNativeLibraryのメソッドをコールするjavaクラスを追加して、そのクラスへのバインディングクラスを作成してコールする形式をとってみました。

f:id:tamafuyou:20161121221708p:plain

まずはjavaファイルをプロジェクトに追加してビルドアクションをAndroidJavaSourceに変更します。

package aquestalk;

public class AquesTalk {
    static {
        System.loadLibrary("AquesTalk");
    }
    /**
    * 音声記号列から音声データを生成します。JNI実装(native修飾子)
    * <p>発話速度は通常の速度を100として、50 - 300 の間で指定します(単位は%)。</p>
    * @param kanaText 音声記号列(UTF-8)
    * @param speed 発話速度(%)
    * @return wavフォーマットのデータ
    */
    public synchronized native byte[] syntheWav(String kanaText, int speed);

}

中身はこんな感じです。

次にバインディングクラスを作成します。

作成したクラスはこちらです。

AquesTalk_Xamarin_Sample/AquesTalk.cs at master · yuka1984/AquesTalk_Xamarin_Sample · GitHub

Working With JNI - Xamarin

このあたりのサンプルコードをベースに作成しました。

ちょっと悩んだのはこの関数です。

        [Register("syntheWav", "(Ljava/lang/String;I)[B", "GetsyntheHandler")]
        public virtual unsafe byte[] synthe(string kana, int speed)
        {
            if (id_syntheWav == IntPtr.Zero)
                id_syntheWav = JNIEnv.GetMethodID(ClassRef, "syntheWav", "(Ljava/lang/String;I)[B");
            try
            {
                var ptr = IntPtr.Zero;
                if (GetType() != ThresholdType)
                    ptr = JNIEnv.CallNonvirtualObjectMethod(Handle, ThresholdClass, JNIEnv.GetMethodID(ThresholdClass, "syntheWav", "(Ljava/lang/String;I)[B"), new JValue(new Java.Lang.String(kana)), new JValue(speed));
                else
                    ptr = JNIEnv.CallObjectMethod(Handle, id_syntheWav, new JValue(new Java.Lang.String(kana)), new JValue(speed));
                return JNIEnv.GetArray<byte>(ptr);
            }
            finally
            {
            }
        }

RegisterAttributeの第2引数、JNIEnv.GetMethodIDなどでメソッドのシグネチャを指定する必要があります。

要するにメソッドの形ですね。

Determine the signature of a method - Real's Java How-to

このページが例がついててわかりやすいです。

今回のメソッドは byte[] syntheWav(String kanaText, int speed)なので (Ljava/lang/String;I)[Bとなるわけです。

そして関数の実行と戻り値の受け取りはJNIEnv.CallxxxMethidを使用します。

JNIEnvクラスにはintとか単純な方はCallintMethodみたいな感じでコール用の関数が用意されているのですが戻り値が配列の場合には

CallObjectMethiodを使用してIntPrtを受け取りJNIEnv.GetArrayで配列に変換します。

MatinActivityを作成する

Javaで書かれているMainActivityクラスをC#に移植します。

単純作業でよいと思います。

AquesTalk_Xamarin_Sample/MainActivity.cs at master · yuka1984/AquesTalk_Xamarin_Sample · GitHub

実行

評価版はsoファイルがarmeabi用だけしかないのでAVDでarmeabiのバーチャルデバイスを用意します。

f:id:tamafuyou:20161121224415p:plain

Android2.3にて作成する必要がありAPIレベルが10になってしまいますのでプロジェクトのMinimum Android to Targetを変更します。

f:id:tamafuyou:20161121224612p:plain

以上で実行すれば ゆっくりしていってね!!!ってしゃべると思います。

以上、次回はXamarin.iOSゆっくりしていってね!!!ってしてみたいと思います。