LoginSignup
6

More than 1 year has passed since last update.

【Rust】UTAUプラグインを作ってみる話。(1/3) ~API作成編~

Last updated at Posted at 2022-01-06

はじめに

Qiita記事初投稿のディーノです。どうもこんにちは。
突然なんですが、みなさんUTAUってご存知でしょうか。
簡単に説明すると、いわゆるボカロのフリー版みたいなもので、誰でも無料で歌唱合成ができてしまうとんでもツールです。

そんなVOCALOID Editor殺しかと思われるUTAUですが、UTAU本体のみでは機能に乏しかったり、UXが良くなかったりなどの問題があります。
そのためUTAUでは、ユーザーが簡単に機能を追加できるようにプラグインを挿す(?)ことができます。
この記事ではそんなUTAUプラグインを作っていきます。

ちなみに自分はプログラミング歴1年未満の超初心者です。
使える言語は未だCとRustの二択なので、できることもそれなりに限られています。
今回使うのはRustオンリーです。
また以降はRustとUTAUを使用したことがあることを前提に進めていきます。

UTAUプラグインを作るための予備知識

さぁいざプラグイン作るぞと意気込んでも、やっぱり何からすればいいのか分かりません。ということで、UTAU プラグイン 作り方でググります。
その結果参考になった記事をば。

「UTAUユーザー互助会」は事細かく説明されている感じがするので、コーディングをしながら分からなかったところをピンポイントで調べると良さそうな気がしました。
逆に「プラグイン製作のススメ」は言語こそ違いますが、一連の流れが掴めるので自分は真っ先に見ました。動画であることも情報が入りやすくてありがたいです。
そして極めつけに「半音上げプラグイン大会の振り返り」でサンプルコードを見れば多分流れは掴めそうです。

ということで、自分なりのUTAUプラグイン処理の大まかな流れをば。

  1. UTAUからTMPファイルのパスをプログラムの引数として受け取る。
  2. TMPファイルからノートの各パラメータ(長さ、歌詞、音程、etc)を抽出する。
  3. 抽出したパラメータに任意の処理を実行する。
  4. 変更を加えた各パラメータをTMPファイルに書き込む。

こんな感じでしょうか。
とすると、1に関してはstd::envでどうにかなるとして、少なくとも2、3、4についての関数を作る必要がありそうです。

UTAU API for Rust的なのを自作する

では早速作っていきましょう。
とはいっても、上のようなことをするとなると、それなりに処理が複雑になりそうです。
自分の脳みそは然程マルチスレッドではないので、ここで本命からクレートを分けようと思います。
今後複数回UTAUプラグインをRustで作りたいと思ったときにも、外部クレートがあるとなれば便利ですからね。

ということでできたものがこちらです。

exampleはこんな感じ。

src/main.rs
use utau_rs::*;

fn main(){
    half_upper();
}

fn half_upper(){
    //オブジェクトの生成(TMPファイルの読み込み・各パラメータの抽出)
    let mut uta_sections=UtaSections::default();

    //セクションごとの処理
    for section in uta_sections.sections.iter_mut(){
        section.note_num+=1;
    }

    //TMPファイルへの書き込み
    if let Err(err)=uta_sections.write(){
        panic!("Error:{}\n",err);
    }
}

老練のRustaceanならexampleを見ただけでお粗末なコードだと思われるかもしれませんが、そこはどうかご了承ください。
動けばよかろうなのです。

それはさておき、コードの説明をしましょう。


use utau_rs::*;

今回作ったクレートであるutau_rs以下のモジュール全てをスコープへ持ち込みます。
丁寧なクレートならちゃんとモジュール名を書くと思いますが、今回はcrates.ioにあげるわけでもないので、これでいいことにします。


let mut uta_sections=UtaSections::default();

UtaSectionsオブジェクトを生成します。
UtaSectionsオブジェクトはdefaultトレイトを実装しているので、new()とかread()とかをする必要はありません。
しかしnew()read()pubで定義しているので、

let mut uta_sections=UtaSections::new().unwrap();
uta_sections.read().unwrap();

とすることもできます。ケースバイケースで使っていただければと思います。


for section in uta_sections.sections.iter_mut(){
    section.note_num+=1;
}

UtaSections型のメンバ変数であるsections.iter_mut()で可変なイテレータとし、それをfor in構文で回します。
ここで、sectionsVec<UtaData>型なので、そのイテレータの各要素となるsectionUtaData型になります。
この型はメンバにLengthやLyrics等TMPファイルの情報を格納した変数を持ちます。
また、ただ回すだけなら追加するメソッドは必要ないのですが、section.note_num+=1;をする必要があるため.iter_mut()を付け加えます。
これにより、各セクション(各ノート)のNoteNum(音程)を一つ増加させることができます。


if let Err(err)=uta_sections.write(){
    panic!("Error:{}\n",err);
}

uta_sectionsに格納されている情報をTMPファイルに書き込みます。
ちなみにif let Err(err)=uta_sections.write(){}という他言語ではあまり見かけない(と自分が勝手に思っている)構文ですが、これはuta_sections.write()Err(err)を返したときにのみ{}の処理が実行されるというものです。

さて、ここまでで最低限のクッションとなるクレートが出来上がりました。
これは先述の通りcrates.ioではなくGitHubの公開リポジトリとして保存しているので、自分のプロジェクトで使う際には

Cargo.toml
[dependencies]
utau_rs = {git = "https://github.com/dino3616/utau_rs", branch = "main"}

という記述を追加してください。
今後クレートの仕様が変わるようなアップデートはしない予定ではいますが、もしそのような場合には今度こそちゃんとcrates.ioにあげてバージョン管理をするようにします。

おわりに

今回はUTAUプラグインのおおまかな流れとUTAU API for Rustを自作するところまで進めました。
次回は今回作成したAPIをもとにCUIでのプラグイン作成をしようと思います。

またね。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6