【UiPath】しゃべるアクティビティを作る

引き続きカスタムアクティビティ作りで遊んでみます。
前回＞[[カスタムアクティビティのプロパティを見える化する](https://qiita.com/UmegayaRollcake/items/ca6108560e3b1e183780)]

せっかくロボットが人間の代わりに働いてくれるので、
　:computer:「今からXXXX作業を始めます」
とか喋ってから作業をしてくれるとそれっぽいよね。
という事で、指定したテキスト通りにしゃべってくれるアクティビティを作ります。

喋るアクティビティは既にV.Vaidyaという方が [VVaidya.TextToSpeechActivities](https://gallery.uipath.com/packages/VVaidya.TextToSpeechActivities/) として公開してるけど、
日本語テキストに対応していないので自分で作るしかないのです。

# 事前準備：Speech Platform 11の用意（Win7の場合）
恥ずかしながら私のPCはまだWindows7で、Win7は標準で日本語の音声合成機能が無いようなので
[こちらのサイト](http://mahoro-ba.net/e1541.html)を参考に、Microsoft Speech Platform 11+HarukaをPCに導入します。
※このサイト、"簡単インストーラー"なるものまで用意されていたので利用させていただきました。

![install.png](https://qiita-image-store.s3.amazonaws.com/0/255097/cc78c188-3063-c00a-c06c-3da1c22105b1.png)

# アクティビティデザイナーの実装
アクティビティデザイナーの実装の仕方は[前回](https://qiita.com/UmegayaRollcake/items/ca6108560e3b1e183780) [前々回](https://qiita.com/UmegayaRollcake/items/4fe69c00e82402cc478b)で触れているのでざっと流します。

まずMS Expression Designerでアイコン絵を描いて、
"XAML WPF リソースディクショナリ"形式でXAMLファイルをエクスポート。
![speechicon.png](https://qiita-image-store.s3.amazonaws.com/0/255097/ba77935e-dfeb-dbb4-8072-5a81d6c51b4a.png)

次にVisual Studioで新規にアクティビティデザイナー(SpeechTextDesigner.xaml)をプロジェクトに追加し、
XAMLペインに\<sap:ActivityDesigner.Icon>と\<DrawingBrush>、その中にExpression Designerから
エクスポートしたXAMLファイルの中の\<DrawingBrush.Drawing>タグ以降を貼り付ける。
最後にExpressionTextBoxのくだりを追加すればアクティビティデザイナー側は完了。

とまぁ文章で説明するとわかりにくいので、↓に全文載っけました。

``` SpeechTextDesigner.xaml
<sap:ActivityDesigner x:Class="Umemaru.SpeechTextDesigner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:System;assembly=mscorlib"
    xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
    xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
    xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation">

    <sap:ActivityDesigner.Icon>
        <DrawingBrush>
            <DrawingBrush.Drawing>
                <DrawingGroup>
                    <DrawingGroup.Children>
                        ~中略~
                    </DrawingGroup.Children>
                </DrawingGroup>
            </DrawingBrush.Drawing>
        </DrawingBrush>
    </sap:ActivityDesigner.Icon>

    <sap:ActivityDesigner.Resources>
        <ResourceDictionary>
            <sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
        </ResourceDictionary>
    </sap:ActivityDesigner.Resources>

    <DockPanel>
        <sapv:ExpressionTextBox OwnerActivity="{Binding Path=ModelItem}" ExpressionType="s:String" 
            HintText="Text must be quoted." MaxLines="1"
            Expression="{Binding Path=ModelItem.Text, Converter={StaticResource ArgumentToExpressionConverter}, 
            ConverterParameter=In, Mode=TwoWay}"/>
    </DockPanel>
    
</sap:ActivityDesigner>
```

# Speech Object Libraryの参照設定とnupkg化の注意点
アクティビティのコードを書く前に、プロジェクトにSpeech Object Libraryを参照登録します。

まずプロジェクトの参照設定-COMに"Microsoft Speech Object Library 11.0"を追加。
![com.png](https://qiita-image-store.s3.amazonaws.com/0/255097/9734c6a9-7c65-4452-c591-38185dd9aa93.png)

追加すると参照ツリーの下に"SpeechLib"が出てくるので、"相互運用型の埋め込み"を**False**に変更。
　![property.png](https://qiita-image-store.s3.amazonaws.com/0/255097/cdde5c2a-125c-20e4-b325-4fb39ff5f1ec.png)

※このプロパティはデフォルトでTrue(=アクティビティのDLLに埋め込む)になってますが、
それだとnupkg化してUiPathに食わせても、アクティビティのTreeに出てきません。
`System.BadImageFormatException: Format of the executable (.exe) or library (.dll) is invalid.`
というエラーがUiPath Studioのログ(Studio.log)に出力されます。
つまりSpeechLibを埋め込んだDLLはUiPathが正常に食えないようです。

False（=埋め込まない）にしてビルドすると、SpeechLibの正体であるInterop.SpeechLib.dllが
プロジェクトのDLLとは別に出力されます。
![dll.png](https://qiita-image-store.s3.amazonaws.com/0/255097/9dca9858-79cc-b680-6dad-ed7eaddac8ed.png)

nupkg化する際にこの**Interop.SpeechLib.dllもlibに追加する**必要があります。
ここが非常に大事なポイントです。
![pupkgka.png](https://qiita-image-store.s3.amazonaws.com/0/255097/d16ec1c8-2460-8c55-c00c-7a5c9d55c357.png)

最初はこれに気づかずにプロジェクトのDLLだけでnupkg化したら、実行時に
"Interop.SpeechLib.dll"が無い、とエラーになりました。
UiPath的にはDLLに埋め込むのはダメだけど、nupkgには含める必要があるという事です。
![error.png](https://qiita-image-store.s3.amazonaws.com/0/255097/e0e2c7f3-ca49-e7e0-5ecb-ca9dc4edd006.png)

# アクティビティクラスの実装
順序が逆になりましたが、肝心のアクティビティクラスを実装します。
ここでも[このサイト](http://mahoro-ba.net/e1590.html)のお世話になります。
Windows form版のサンプルコードを公開されていたので、それを参考にします。

参照登録したライブラリSpeechLibをusingして、そのオブジェクト群を使って喋らせます。

``` SpeechText.cs
using System;
using System.Activities;
using System.ComponentModel;
using SpeechLib;

namespace Umemaru.Activities
{
    [Category("Umemaru.Activities")]
    [DisplayName("Speech Text")]
    [Designer(typeof(SpeechTextDesigner))]    
    public class SpeechText : CodeActivity
    {
        SpVoice speaker;// 音声合成オブジェクト
        ISpeechObjectTokens info;// 音声情報

        [Category("Input")]
        [RequiredArgument]
        public InArgument<string> Text { get; set; }

        protected override void Execute(CodeActivityContext context)
        {
            speaker = new SpVoice();
            info = speaker.GetVoices("", "");
            speaker.Voice = info.Item(0);// Haruka
            speaker.Volume = 100;// 音量の設定(0~100)
            speaker.Rate = -1;// 速度の設定(-10~10、0だとちょっと速い)

            speaker.Speak(Text.Get(context), SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak);
        }
    }
}
```

ここまでやれば、無事にSpeech Textアクティビティが実行できます。ちゃんと漢字も喋ってくれます。
（しかし声がロボットっぽくないんだよなぁ…昔の勇者ロボみたいなのがいいんだけど）
![tree.png](https://qiita-image-store.s3.amazonaws.com/0/255097/7c186b5a-ff2d-7ff0-41bd-eae8c45e9402.png)　![speech.png](https://qiita-image-store.s3.amazonaws.com/0/255097/2b3f6570-80a0-81e1-c081-996068f91957.png)

使いどころとしては、ワークフローの先頭だけでなくエラー時の対応にも使えるかな。
例えばSelectorで指定したボタンが見つからず押せない時は、try~catchのExceptionで
　:computer:「だっ誰かっ、誰か俺の代わりに[登録]ボタンを見つけて押してくれぇぇ！」
と叫ばせて人を呼ぶとか。

---
今回はここまで。

今はWin7マシンなのでMicrosoft Speech Platform 11を追加インストールして使いましたが、
Win8以降はデフォルトで違う音声合成技術が搭載されているそうなので、
次にWin10マシンに替えたらそっちを使うように作り直さなきゃな・・・

その時はWin10向けの内容で記事追記します。