半音上げプラグイン大会UTAU プラグインの作り方は開発者ごとに異なり、その違いを見てみると楽しく勉強になるのではないか、ということで始まった「半音上げプラグイン大会」。UTAU プログラマーの祭典です。2015/12/11~12/31 の 3 週間開催。

ニッチなイベントなので、そもそも参加してくださる方がいるのか不安でしたが、蓋を開けてみれば大盛況で、なんと 24 件ものエントリーがありました。単に「半音上げる」だけのシンプルなプラグインが、いろいろなプログラミング言語やアルゴリズムで、24 パターンも登場したということです。

本記事では、そんな大会の様子を振り返ってみたいと思います。大会レギュレーションは、以前の記事を参照して下さい。

蛇足ですが、UTAU のメニューに半音上げる処理はありますので、実用上は、半音上げるプラグインは必要ありません。

目次


エントリー一覧

#エントリー(敬称略)言語ソースコード
1HaruqaUWSC表示 DL
2
なでしこ
表示 DL
3SHINTA
C#
表示 DL
4
bizz
Lazarus
表示 DL
5
masao
C++
表示
6
bizz
Lazarus
表示 DL
7
masao
C
表示
8
Haruqa
UWSC
表示 DL
9
Haruqa
UWSC
表示 DL
10
大兄P
Java
表示
11
bizz
HSP
表示 DL
12
山岸
Java
表示
13
masao
C++
表示
14
大兄P(#10 の修正)
Java
表示
15
ばわめ rwxr--r--
C#
表示(メイン クラス
16
あさくら・ふにょし
Electron
表示(HTML JavaScriptDL
17
あさくら・ふにょし
バッチファイル
表示
18
Haruqa
JScript
表示
19
とりフラ.ust.vvv
ExcelVBA
表示
20
masao
C++
表示
21
あらいふ
Python
表示 DL(ソース バイナリ
22
Haruqa
C#
表示 DL
23
飯香
条件設定プラグイン
表示
24
飯香
BF プラグイン
表示

典型的な半音上げプラグインの動作

まず、UTAU プラグインそのものの概要をおさらいしておきましょう。

一般的なアプリケーションにおいて「プラグイン」というと DLL 形式のものが多いですが、UTAU の場合はそうではなく、UTAU プラグインは実行ファイル(.exe)などの形式となります。

UTAU プラグインの仕様は公式サイトなどに書かれていますが、UTAU 画面上でユーザーが選択した音符の情報を、UTAU が一時ファイルに書き出します。UTAU プラグインはその一時ファイルを読み込み、音符の情報を変更して、一時ファイルに書き戻します。プラグイン実行後、UTAU が再度一時ファイルを読み込んで変更内容を確認し、UTAU 画面に反映する、という流れです。

一時ファイルは INI ファイルの形式となっており、1 つの音符の情報が 1 つのセクション(UTAU ではセッションと呼んでいます)にまとめられています。例えば、音程が「ド」(C4)で歌詞が「い」の四分音符だと、以下のように出力されます。

[#0001]
Length=480
Lyric=い
NoteNum=60
Intensity=100
Moduration=0

NoteNum が音程を表していて、数字が大きいほど高音になります。60 なら C4、61 なら C#4 というように、1 増える毎に半音上がっていきます。

つまり、「半音上げプラグイン」であれば、NoteNum を読み、NoteNum の値を 1 増やしてから、一時ファイルを上書きすれば、目的を達することができるわけです。

次節以降で、今回エントリーされた内容について、私の分かる範囲で解説していきます。

解説:C#

まずは、エントリー No.3、私が作成した C# 版の半音上げプラグインについて。

処理方法は単純で、一時ファイルを全行読み込んでから、NoteNum を探し、値を 1 増やしています。

注意すべきは、[#PREV][#NEXT] という特別なセクションです。UTAU は、ユーザーが選択した音符の「前後の音符」の情報も一時ファイルに出力し、その内容が [#PREV][#NEXT] セクションに記載されます。[#PREV][#NEXT] セクションの内容を書き換えてしまうと、ユーザーが選択してない音符の情報が変わってしまうことになるので、避ける必要があります。

セクション行を解析する際に、選択範囲の音符セクション([#0001][#0002]……)かどうかを判定し、結果をフラグとして保持しておきます。NoteNum が出てきた時にフラグを確認し、フラグが立っている場合のみ、処理を行うことで、[#PREV][#NEXT] セクションを避けています。

メモリ上で NoteNum の変更を終えた後、全行を一気に一時ファイルに書き戻しています。

別の C# エントリーとして、ばわめ rwxr--r-- さんの No.15 があります。

この実装では、一時ファイルへのアクセスを INI ファイル解析クラス(をカスタマイズしたもの)を用いて行っています。これにより、メイン側では処理をセクション単位で遷移させることができるようになり、また、NoteNum の値も簡単に取り出せるようになっています。

音符セクションかどうかの判定には正規表現を用いており、確実な判定が行えます。

C# はもう 1 つ、Haruqa さんの No.22 もあります。

この実装も No.15 と同様、一時ファイルへのアクセスを担当するクラスを用いていますが、こちらはイチから作成したクラスとなっており、音符のプロパティーをそれぞれ専用のメンバー変数で保持する仕様となっています。

解説:C++

C++ は masao さんが 3 エントリーされています。

ますは No.5

本人コメントでは、先ほどの No.3 C# 版を移植しただけとありますが、処理方法はかなり進化しています。

一時ファイルを一行一行読みながら作業するのですが、まず、音符セクションが出てきた場合に、その行を「出力バッファ」に溜めます。また、NoteNum が出てきた場合にも、音程を半音上げてから「出力バッファ」に溜めます。それ以外の場合は、出力バッファに溜めません。

結果として、最小限の変更箇所のみが「出力バッファ」に溜まり、それを一時ファイルに書き戻しています。これにより、書き戻す一時ファイルがコンパクトになります。

[#PREV][#NEXT] を除外するフラグの考え方は No.3 と同様ですが、判定方法が賢くなっています(No.3 はかなり手抜きでした)。

次に No.13

こちらは、一時ファイルの取り扱いを楽にする Lib UTAU を用いて実装しています。

一時ファイルの解析を Lib UTAU が行ってくれるので、[#PREV][#NEXT] を除外するのも簡単で、プログラムが非常にシンプルになっています。Lib UTAU の SectionNotesNormalBegin()~SectionNotesNormalEnd() の間だけ処理を繰り返すことで、自動的に [#PREV][#NEXT] が除外されます。

最後に No.20

Lib UTAU と同様の自作ライブラリを用いて実装しているとのことで、No.13 同様、プログラムが非常にシンプルになっています。

自作ライブラリは非公開のため内容は不明ですが、セクションとキーバリューペアの関連性も保持しているようで、使いやすそうです。

解説:C

masao さんは No.7 で C 言語版も作成されています

基本的な考え方は No.5 C++ 版と同様ですが、「出力バッファ」を使わずに、代わりに、逐次「テンポラリファイル」(UTAU の一時ファイルとはまた別のファイル)に書き出しています。C 言語は文字列(に関わるメモリ)の扱いが面倒なので、出力バッファを使わないほうが楽だと判断したのでしょう(違ってたらすみません)。

最後に、UTAU の一時ファイルを削除して、代わりに、プラグインが出力したテンポラリファイルを一時ファイルにリネームして完了です。

C 言語はファイルや文字列の扱いが煩雑なのに、非常にスマートにプログラミングされており、masao さんの技術力の高さがうかがえます(私はこんなにスマートに実装できません)。

解説:Java

Java はお二方がエントリー。

No.12 の山岸さんは、処理の流れとしては No.7 の C と近しい感じになっています。

C とは異なり、Java は文字列を手軽に扱えるので、全体的にコードがすっきりしています。また、音符セクションかどうかの判定や、音高行の判定に正規表現を用いており、スマートに判定ができています。

エラー処理が入っている点も見逃せません。本大会はプラグインの実運用が目的ではないので、エラー処理が入っていないエントリーも多いですが、実際にプラグインを開発する際にはエラー処理は必須になってきます。きちんとエラー処理を行う山岸さんの堅実さがうかがえます。

No.14 の大兄 P さんは、一風変わった処理の流れになっています。

最初に一時ファイルを 7 行読み飛ばしているのは、[#VERSION][#SETTING] といったセクションを読み飛ばす意図だと思われます(違っていたらすみません)。

その後、[#PREV][#NEXT] を避けつつ、NoteNum を 1 上げています。

No.14 は修正バージョンで、当初は No.10 でのエントリーでした。自分のソースコードを見直したり、他人のコードを参考にしたりしながら、ブラッシュアップしていくのも、プログラミングの醍醐味ですよね。

解説:Python

No.21 のあらいふさんは Python で作成されています

行毎に処理を進めていくのですが、「セクション開始待ち」なのか「NoteNum エントリー待ち」なのかという「状態」を保持しておき、状態に応じて、解析を実行する関数を切り替えています(関数ポインタ方式)。

「セクション開始待ち」では、セクション名を見て、音符セクションであれば、「NoteNum エントリー待ち」に状態遷移します。

「NoteNum エントリー待ち」では、NoteNum の行が来たら音程を 1 上げて、「セクション開始待ち」に遷移します。

ファイルの存在確認や、音程の上限確認など、エラー対応関連も充実しています。

解説:UWSC

これまでは、プログラミング言語人気トップ 10 に入るような超メジャーな言語の実装を見てきましたが、ここからは、もう少しいろいろな言語を見ていきましょう。

UWSC では Haruqa さんが 3 件エントリーしています。

今回の大会である意味一番驚いたのが No.1 の UWSC です。このプラグインは、一時ファイルを一切使いません。UTAU を操作し、UTAU のメニューから「選択部分を上へ移動」を実行することにより、半音上げを実現しています。UTAU 本体を操るプラグインというわけです。

No.8 は一時ファイルを使いますが、一時ファイルの書き換えは、メモ帳を操って行っています。

No.9 は普通に一時ファイルを更新しています。

解説:なでしこ

巽さんが使ったなでしこは、日本語でプログラミングが行える言語です。

処理方法としては、いったんすべての NoteNum を 1 上げたあとで(UTAU とは別の)テンポラリファイルに保存し、その後、テンポラリファイルから UTAU の一時ファイルに書き戻す際に、[#PREV][#NEXT] を除外しているようです。

個人的には、「B はそれ」という禅問答みたいは表現が、日本語プログラミングっぽくて好きです。

解説:Lazarus

オープンソース版の Delphi ともいえる Lazarus(言語としては Object Pascal)は、bizz さんが 2 件エントリーされています。

No.4 はオーソドックスなバージョンで、処理の流れは No.5 の C++ とほぼ同じです。

変わり種は No.6

半音上げるという処理を、音程を上げるのではなく、ピッチベンドカーブをいじることによって実現しています。ピッチベンドは PBW や PBY といったパラメータをかなり処理する必要があり、ソースコードの量からもその大変さがうかがえます。

「やり方は 1 つではない」ということがよくわかる実例です。

解説:HSP

bizz さんはもう 1 つ、HSP でもエントリー。

HSP は構文が簡単な手続き型スクリプト言語ですが、コンパイルが可能なため、利用者側は HSP のインストールが不要です。小規模なゲーム開発に人気なイメージがあります。

HSP は目的語を省略できるタイプの言語のようで、内容がいまいち読み取りにくいのですが、処理の流れとしては、No.3 C# 版の全行書きだし型に近いのではないかと見受けられます(違っていたらすみません)。

解説:Electron

あさくら・ふにょしさんは Electron でエントリー。私は今回の大会で初めて Electron の存在を知りましたが、HTML + JavaScript で(ローカルで動く普通の)アプリを作れる環境のようです。Web でバリバリコーディングしている人が、ちょっとしたツールを作る場合などに重宝しそうです。

実装は、最近の JavaScript らしくラムダ式を用いています(幹となる部分)。

処理内容としては、最初に「セクション名」と「キーバーリューペア」のペアをすべて解析して連想配列に格納します(連想配列の入れ子のようなイメージ)。本記事冒頭の一時ファイルで言えば、{#0001={Length=480, Lyric=い, NoteNum=60}} みたいな感じです。JavaScript の柔軟な型の利用が活きています。

その後、音符セクションについて、NoteNum を 1 上げてから、テンポラリファイルに書き戻します。

解説:バッチファイル

あさくら・ふにょしさんはバッチファイルでもエントリー。

いにしえの MS-DOS 時代から省力化ツールとして親しまれているバッチファイルですが、UTAU プラグインとしても使えるのだと思うと、隔世の感があります。

さすがに高度な関数は使えないので、読み飛ばすセクション([#PREV][#NEXT] など)は列挙して対応していますが、バッチファイルだけでも文字列の扱いや音程計算がきちんと行えており、驚きの内容になっています。私は、バッチファイルでこんなにうまく実装できるものとは知りませんでした。

UTAU プラグインとしてだけではなく、日々の作業の省力化のためにバッチファイルを書く際にも非常に参考になる内容になっています。

解説:JScript

UWSC の Haruqa さんは JScript でもエントリー。JScript は JavaScript 互換ですが、JavaScript 単体では通常行えないファイル出力などが可能です。

処理の内容としては、一時ファイルを一行ずつ読み込みながら、NoteNum であれば半音上げつつ、バッファに溜めていきます。最後に、一時ファイルに書き出します。

解説:ExcelVBA

とりフラ.ust.vvv さんは ExcelVBA でエントリー。いわゆるエクセルマクロです。

処理の流れは、No.12 の Java と近しい感じになっています。VBA は Visual Basic for Applications という名前の通り、BASIC 言語の流れを汲んでいますが、ファイルオープンの構文などはまさに BASIC 感に溢れています。

さすがにエクセルファイル直接は UTAU プラグインとして使えないようで、実際に使う際には、バッチファイルと組み合わせているようです。

解説:既存プラグインのパラメーター設定方式

さまざまなことが行える既存の UTAU プラグインに対して指令を与えることで、半音上げるという作戦もあります。「プラグインを開発する」のではなく、「既存のプラグインでまかなう」というイメージですね。

飯香さんの No.23 は、条件設定プラグイン(亜麻)を、すべての音符に対して半音上げるという条件を設定して起動しています。

また、No.24 は、特定の効果を持つ音符の配置を予め BF プラグインに読ませてから、再度 BF プラグインを起動することで、半音上げを実現しています。BF は恐らく Brainfuck のことだと思いますが、難解なパズルです。

まとめ

「半音上げる」。ただそれだけのことなのに、まさに十人十色の実装になりました。

行ごとに文字列的な処理をしていく方法が主流でしたが、人や言語によって実装方法はさまざまでした。また、セクションとその値を有機的に捉えたうえで半音上げるプラグインもいくつかありました。

さらには、音程ではなくピッチをいじるプラグインや、UTAU プラグインの概念を根底から覆すプラグインまで登場しました。

他人のプログラムを見ると言うことは非常に勉強になりますが、今回はシンプルで身近な UTAU プラグインを題材としたことで、より一層そのことが実感できたかなと思います。

ご参加くださった皆様、本当にありがとうございました。

遅刻組

締切は過ぎましたが、投稿いただいたものをこちらに掲載させていただきます。

関連リンク


更新履歴

  • 2020/03/28 [28]~[30] 捕捉