【UTAUプラグイン】半音上げプラグイン大会にお世話になったので恩返ししてみるの巻 with utauPlugin＆MaterialDesignInXAMLToolkit

UTAUプラグインを簡単に作るための「utauPlugin」と
見た目をイイ感じにするための「Material Design In XAML Toolkit」を使って
「例のアレ」を作ります。

# はじめに
UTAUプラグイン作りのためプログラミングを始めて早1年3ヶ月。
Twitterに教えてくれる方がたくさんいたりDiscordにプログラミング相談所があったりするおかげで、UTAUプラグインとか関連ツールとか全然関係ないAndroidのアプリとか色々作れるようになって楽しいです。時間が足りない。

完全に先人たちのおかげでここまで来れたわけですが、[「【UTAU】半音上げプラグイン大会」](https://togetter.com/li/913880)というのがありまして、これがサンプルコードの宝庫になってます。
その後、我らが[デルタ先生](https://twitter.com/delta_kuro)がC#でUTAUプラグイン作る用のクラスライブラリ「[utauPlugin](https://github.com/delta-kimigatame/utauPlugin)」というのを作ってくださって、これ無くしてはプラグイン作りとかやってられないぐらい便利なので、
今回はこれを使って半音上げプラグインを作っていこうと思います。

ついでなので見た目もイイ感じにします。

# 作りたいもの概要
・半音上げプラグイン
→音程を半音上げるだけのプラグイン
　実用性は特にないけどシンプルなのでサンプルにぴったり

・言語はC#、開発環境はVisual Studio Community 2019
→Windows用ソフト作るときのベーシックな環境（たぶん）

・WPF
→C#で作れるものは色々あって、操作画面が必要なプラグインは「WPF」で、画面がいらないプラグインは「コンソールアプリ」で作るのがいいと思う

・「[utauPlugin](https://github.com/delta-kimigatame/utauPlugin)」とは
→UTAUが出力したデータを読んで、扱いやすく整理し、処理後のデータを本体に返してくれる便利なやつ

・「[Material Design In XAML Toolkit](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit)」とは
→WPFの見た目のパーツたちを「マテリアルデザイン」にしてくれるやつ
マテリアルデザインとはGoogleが推してるUIのデザインの考え方で、立体感やアニメーションがついててかわいい
某Windowsのペラペラなボタンとは段違いである

#作り始める前に
「[C#でUTAUプラグイン開発を始める方法](https://ch.nicovideo.jp/delta_kimigatame/blomaga/ar1716787)」にutauPluginの導入のしかたが書いてあります。
というかコンソールアプリ版半音上げプラグインの作り方がまんま書いてあります。
HelloWorldか？ってぐらいの簡素さでプラグインが書けます。

Material Design In XAML Toolkitに関しては、Visual StudioのNuGetで「MaterialDesignThemes」と検索して出てくるやつをインストールしてください。
[GitHub](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/releases)にDemoAppがあるので落としとくと便利です。

C#の知識的には基本的な代入や四則演算とfor、if、Listぐらいわかれば十分かと。
配列はグループ機能的なやつですがListが駆逐したんで（してない）Listだけ覚えればとりあえず大丈夫です。

# 完成品
![プラグイン画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609825/d9f28352-c841-842f-23aa-8abf7342e87b.png)
[配布所](https://1drv.ms/f/s!AgzVdoePq73CjFvQnjU067_tHeCA)

# 実際のコード

### App.xaml
```xml:App.xaml
<Application x:Class="OneUp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:OneUp"
             StartupUri="MainWindow.xaml"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Application.Resources>
        
        <!-- MaterialDesignThemeの設定を読み込む（基本色を選ぶ） -->
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Lime.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Amber.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
         
    </Application.Resources>
</Application>
```
ソフトの基本設定みたいなのが書いてあるApp.xaml
`<ResourceDictionary Source`　～の一行目でLightかDarkか選ぶ
2行目で各パーツのデザインをロード（たぶん）
3行目で基本色を、4行目でサブの色を選ぶ

~~OneUpとは緑のキノコのことです~~

### MainWindow.xaml
```xml:MainWindow.xaml
<Window x:Class="OneUp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OneUp"
        mc:Ignorable="d" FontSize="13"
        WindowStartupLocation="CenterScreen" ResizeMode="CanResizeWithGrip"
        Title="半音上げ" Height="200" Width="470"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Grid>
        <!-- ウィンドウ内を上下に分ける -->
        <!-- 下半分のサイズはボタンに合わせて、上半分のサイズは残り全部 -->
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>

        <!-- 上半分 -->
        <!-- StackPanelに入れちゃえばだいたい何でも揃う -->
        <StackPanel Margin="20">
            <TextBlock TextWrapping="Wrap">
                <Run Text="↓Material Design In XAML ToolkitのCheckBoxがかわいいから使いたいだけ" /><LineBreak/>
                <Run Text="　アクセントカラーにしてみたぞ" />
            </TextBlock>
            
            <CheckBox Content="休符も上げる" x:Name="R" IsChecked="True" Style="{StaticResource MaterialDesignAccentCheckBox}" />
        </StackPanel>

        <!-- 下半分 OKとキャンセル -->
        <!-- 横方向に並ぶタイプのStackPanelをGridの下半分に入れる -->
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1" VerticalAlignment="Bottom">
            <Button Content="OK" Name="ok" Margin="10,10,10,20" Width="90" Click="OK" />
            <Button Content="キャンセル" Margin="10,10,20,20" Width="90" Click="Cancel" />
        </StackPanel>

        <!-- DialogとDrawer -->
        <!-- MaterialDesignの機能。ポップアップとかドロワー出すときに使う -->
        <materialDesign:DialogHost x:Name="dialogHost" Grid.RowSpan="2" />

        <materialDesign:DrawerHost x:Name="errorDrawer" Grid.RowSpan="2" >
            <materialDesign:DrawerHost.BottomDrawerContent>
                <TextBlock Margin="20" x:Name="errorText"
                           VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </materialDesign:DrawerHost.BottomDrawerContent>
        </materialDesign:DrawerHost>
    </Grid>
</Window>
```
見た目を書くMainWindow.xaml
HTML手打ちポチポチマンだったから親しみやすさはんぱねぇ
###### Window：
`WindowStartupLocation="CenterScreen" ResizeMode="CanResizeWithGrip"`
ウィンドウを真ん中に出す、ウィンドウサイズを変えられるようにする
だいたいいつもこの設定です

`xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"`
Material Design In XAML Toolkitが用意したコントロールを使いたいなら書く
（コントロールとは、ボタンとかのパーツのこと）

###### GridとかStackPanelとか：
コントロールを入れるためのコントロール
「[WPF 個人用メモ.1](https://qiita.com/Temarin/items/5c02d3597a699aa38d9e)」が参考になります

###### Material Design In XAML Toolkitのコントロールたち：
DialogHost：
今回は使ってませんがUserControlでポップアップ画面を作ってdialogHostに入れて出せばポップアップします
DrawerHost：
下からドロワーがにょきっと出ます
エラーが出たときにドロワーにエラーメッセージ出す用です
（このデザインだとMessageBox似合わないので・・・）
↓こんなやつ
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609825/0856b972-33d9-f112-d622-0538750e7e13.png)

### MainWindow.xaml.cs
```csharp:MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Input;
//忘れずに書いて
using utauPlugin;

namespace OneUp
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        // ここに書いた変数とかはMainWindowクラスのどこからでも呼び出せるよゾーン
        private UtauPlugin utauPlugin;
        // ↓コマンドライン引数ってやつ
        // この配列の0番目にこのソフトの場所が、
        // 1番目にUTAUが出力したプラグイン用データの場所が書いてある
        string[] args = Environment.GetCommandLineArgs();


        public MainWindow() // 起動時に実行
        {
            // たぶんxamlを読んでる
            InitializeComponent();
            // utauPluginを起動してデータの場所を教える
            utauPlugin = new UtauPlugin(args[1]);
            // utauPluginがデータを分析する
            utauPlugin.Input();

            // Enter押す＝OKボタン押すのと同義 にする
            KeyDown += (sender, e) =>
            {
                if (e.Key != Key.Enter) { return; }
                OK(sender, e);
            };
        }

        // OKボタン押すと実行
        private void OK(object sender, RoutedEventArgs e)
        {
            try
            {
                // utauPlugin.noteは音符(Note)がずらーっと入ったList
                // Listの中身をひとつずつ取り出して処理するforeach
                // 今処理してるNoteをnoteと呼ぶ
                foreach (Note note in utauPlugin.note)
                {
                    // 休符かどうか確認
                    if (note.GetLyric() == "R" || note.GetLyric() == "r")
                    {
                        if(R.IsChecked == false) // 休符かつCheckBox「R」にチェックがないとき、
                        {
                            continue; // このNoteの処理をやめて次のNoteにいく
                        }
                    }

                    // 新しい音程は今の音程+1
                    note.SetNoteNum(note.GetNoteNum() + 1);
                }

                // UTAU本体に処理したデータを返す
                utauPlugin.Output();
            }
            catch // try中にエラー吐いたとき、落ちない代わりに↓が実行される
            {
                ErrorOpen("処理に失敗しました"); // この内容は後述
                return; // OKメソッドをやめる（次のCloseは起きない）
            }

            Close(); // ソフトを終了
        }

        // キャンセルボタン押すと実行
        private void Cancel(object sender, RoutedEventArgs e)
        {
            Close();
        }


        //エラードロワーを開く
        public void ErrorOpen(string text)
        {
            // xamlに書いたTextBlockにエラーメッセージを設定
            errorText.Text = text;
            // 警告音を鳴らす
            System.Media.SystemSounds.Exclamation.Play();
            // ドロワーを出す
            errorDrawer.IsBottomDrawerOpen = true;
        }
    }
}
```
MainWindow.xamlと対になる処理部分

# ググるときのTips
余談ですが、UI（見た目）絡みで調べたいことは「**WPF** Button」のように、
それ以外で調べたいことは「**C#** List ソート」のようにググるといいです。

# おわりに
無駄に操作画面作ったり見た目をイイ感じにしたりドロワー出したりしたせいでけっこう長くなりましたが、
ust読んで各音符の情報を仕分けするのが`utauPlugin.Input();`、
必要な情報だけをまとめて何行も出力するのが`utauPlugin.Output();`だけで済むので
UTAUプラグインとしてはめっっっっちゃ簡素になってると思います。
ﾃﾞﾙﾆｬﾝｻｲｺｰ!

ドロワー使わないなら、エラー通知は`MessageBox.Show("エラーメッセージ");`の1行で済みます。
Material Design In XAML Toolkitを導入しないならApp.xamlはデフォルトのままでok。

こんな感じで作ったプラグインたちをうちの[調声メモブログ](https://ameblo.jp/maiko3utau/)で配布してますのでよろしくお願いします。
ハモリ作成とか表情音源ピッカーとかビブラートストッカーとか、
あとUTAU音源をインストーラー型で配布するソフトとかprefix.mapエディタもあるよ。

あと本業は調声なんで[niconicoのマイリス](https://www.nicovideo.jp/mylist/29356584)もよろしくお願いします！