【Rust】UTAUプラグインを作ってみる話。(2/3) ～CUI作成編～

#はじめに
この記事は[こちらの記事](https://qiita.com/dino3616/items/b4054f9b3fc12f60a746)の続きです。

#プラグインを作る(CUI)
予備知識からAPI作成を経て、ようやっと本題に入ります。
今回の最終目標はGUIをもったプラグインの作成ですが、まずは簡単なCUIでプラグインの心臓部分を実装します。

###仕様
まずどんなプラグインを作りたいのかを考えましょう。
先のexampleのような半音上げプラグインもいいですが、もう少し実用的なものにしたいです。
実装が難しすぎず、それでいて単調過ぎない機能がいいですね。
とすると、**ノートを指定したスケール(音階)にスナップさせる**とかいいかもしれません。
そんなこんなで、プロジェクト名は`even_scale`とすることにします。

###実装
では、考えた仕様に沿って関数を実装します。
今後のことも考えて`main()`直書きではなく`even_scale.rs`というファイルに`even_scale()`という関数を実装しましょう。
そしてできたものがこちら。

<details><summary>src/main.rs</summary><div>

```Rust:src/main.rs
mod even_scale;

use std::process;
use utau_rs::*;

fn main(){
    let mut uta_sections=UtaSections::default();

    let selected_scale=even_scale::Scale::C;
    if let Err(err)=even_scale::even_scale(&mut uta_sections,selected_scale){
        eprint!("Error：{}\n",err);
        process::exit(1);
    }

    if let Err(err)=uta_sections.write(){
        eprint!("Error：{}\n",err);
        process::exit(1);
    }
}
```

</div></details>

<details><summary>src/even_scale.rs</summary><div>

```Rust:src/even_scale.rs
use utau_rs::*;

#[allow(non_camel_case_types)]
pub enum Scale{
    C,C_sharp,Cm,Cm_sharp,
    D,D_sharp,Dm,Dm_sharp,
    E,E_sharp,Em,Em_sharp,
    F,F_sharp,Fm,Fm_sharp,
    G,G_sharp,Gm,Gm_sharp,
    A,A_sharp,Am,Am_sharp,
    B,B_sharp,Bm,Bm_sharp,
}

pub struct KeyTone([u32;7]);

const C: u32=24;
const D: u32=26;
const E: u32=28;
const F: u32=29;
const G: u32=31;
const A: u32=33;
const B: u32=35;

impl KeyTone{
    pub fn new(scale: &Scale)->Result<KeyTone,&'static str>{
        Ok(match scale{
            Scale::C      |Scale::Am      =>KeyTone([C,D,E,F,G,A,B]),
            Scale::C_sharp|Scale::Am_sharp=>KeyTone([C+1,D+1,E+1,F+1,G+1,A+1,B+1]),
            Scale::D      |Scale::Bm      =>KeyTone([C+1,D,E,F+1,G,A,B]),
            Scale::D_sharp|Scale::Cm      =>KeyTone([C,D,E-1,F,G,A-1,B-1]),
            Scale::E      |Scale::Cm_sharp=>KeyTone([C+1,D+1,E,F+1,G+1,A,B]),
            Scale::F      |Scale::Dm      =>KeyTone([C,D,E,F,G,A,B-1]),
            Scale::F_sharp|Scale::Dm_sharp=>KeyTone([C+1,D+1,E+1,F+1,G+1,A+1,B]),
            Scale::G      |Scale::Em      =>KeyTone([C,D,E,F+1,G,A,B]),
            Scale::G_sharp|Scale::Fm      =>KeyTone([C,D-1,E-1,F,G,A-1,B-1]),
            Scale::A      |Scale::Fm_sharp=>KeyTone([C+1,D,E,F+1,G+1,A,B]),
            Scale::A_sharp|Scale::Gm      =>KeyTone([C,D,E-1,F,G,A,B-1]),
            Scale::B      |Scale::Gm_sharp=>KeyTone([C+1,D+1,E,F+1,G+1,A+1,B]),
            _=>return Err("不明なエラーが発生しました."),
        })
    }

    fn unwrap(&self)->[u32;7]{
        match self{
            &KeyTone(some)=>some,
        }
    }
}

pub fn even_scale(uta_sections: &mut UtaSections,scale: Scale)->Result<(),&'static str>{
    let tones=KeyTone::new(&scale).unwrap();

    for section in uta_sections.sections.iter_mut(){
        if tones.unwrap().iter().all(|&x|(x%section.note_num)!=0){
            let mut near=section.note_num as i32-tones.unwrap()[0] as i32;
            for tone in tones.unwrap(){
                let tone=match section.note_num{
                    24..=35=>tone+12*0,
                    36..=47=>tone+12*1,
                    48..=59=>tone+12*2,
                    60..=71=>tone+12*3,
                    72..=83=>tone+12*4,
                    84..=95=>tone+12*5,
                    96..=107=>tone+12*6,
                    _=>return Err("不明なエラーが発生しました."),
                };
                if (section.note_num as i32-tone as i32).abs()<near{
                    near=(section.note_num as i32-tone as i32).abs();
                }
            }
            section.note_num=(section.note_num as i32+near) as u32;
        }
    };

    Ok(())
}
```

</div></details>

そこそこ長くなりましたね。
さて、ではコードについて解説しましょうと言いたいところですが、全てのコードに対して解説をしようとなるとなかなか骨が折れるので、要所要所で解説をします。

***

```Rust:src/main.rs
mod even_scale;
```

`even_scale.rs`をモジュールとし、諸々のオブジェクト等々をスコープへ持ち込みます。
`use`のようにも思えますが、`lib.rs`以外のバイナリファイルを`main.rs`に持ち込む際には`mod`の方を使うものだと勝手に思っています。
ここら辺まだ理解が怪しいです。

***

```Rust:src/main.rs
let selected_scale=even_scale::Scale::C;
```

後述の`Scale`列挙体の`Scale::C`を選択したスケールとします。
本当は標準入力に応じて`selected_scale`の値を変えた方がいいとは思うのですが、実装が面倒でした。

***

```Rust:src/even_scale.rs
#[allow(non_camel_case_types)]
pub enum Scale{/*省略*/}
```

`Scale`列挙体を`non_camel_case_types`で定義します。
Rustのenumは基本的に、以下のような`camel_case`という命名規則で定義するのが好ましいとされています。

```Rust:camel_caseなenum
enum Fruits{
    Apple,
    Orange,
    ApplePear,
}
```
ですが`Scale`では`C_sharp`など`camel_case`に則っていない変数が定義されているため、この`#[allow(non_camel_case_types)]`を使用しないとコンパイラが怒ります。
プログラムに影響するわけではないのですが、警告がうざったいという方は~~面倒ですが~~使いましょう。

***

```Rust:even_scale.rs
pub fn even_scale(uta_sections: &mut UtaSections,scale: Scale)
    ->Result<(),&'static str>{/*省略*/}
```

`Scale`列挙体によって指定されたスケールによって`uta_sections.section.note_num`の値を変更します。
ポイントは以下の範囲マッチングです。

```Rust:src/even_scale.rs
let tone=match section.note_num{
    24..=35=>tone+12*0,    //C1～B1
    36..=47=>tone+12*1,    //C2～B2
    48..=59=>tone+12*2,    //C3～B3
    60..=71=>tone+12*3,    //C4～B4
    72..=83=>tone+12*4,    //C5～B5
    84..=95=>tone+12*5,    //C6～B6
    96..=107=>tone+12*6,    //C7～B7
    _=>return Err("不明なエラーが発生しました."),
};
```

ここで`note_num`のオクターヴを判別し、これ以下で値の変更等々を行います。
Cの`switch`とは違い、パターンマッチングは本当に強力な言語機能だと改めて思いました。

###テスト
では実際に動くかどうかテストをしましょう。
今回の実装では`selected_scale`は`even_scale::Scale::C`ですので、スナップするスケールはハ長調(C,D,E,F,G,A,B)であるはずです。

**Before**
![before](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2470169/9fd496f0-24c6-c2ee-6e45-6f64027014ae.png)
**After**
![after](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2470169/3c726781-b6ee-8208-a951-e9b21b13d3e6.png)

大丈夫そうですね。
ちなみに少し話がそれてしまうのですが、テストに使用している音源は「霜月奏凪」という連続音源で、私が自作したものです。
以下のサイトからDLやデモソングの閲覧が可能ですので、興味があればぜひそちらもご覧ください。

https://suraimu3616.wixsite.com/shimotsukikanagi

#おわりに
今回はプラグインの仕様の決定と簡単なCUIによる実装を行いました。
~~プラグインを実行した瞬間に実行が完了するのでUIも何もないですが...。~~
[次回](https://qiita.com/dino3616/items/848523d45877339e58d2)は今回作成したCUIをGUIに拡張しようと思います。

またね。