Lib UTAU ヘルプ
解説
最終更新:
libutau
-
view
サンプルプログラムの解説
Lib UTAU に同梱してあるサンプルプログラムについて、その内容を解説します。
なお、ここで引用しているのはダイジェスト版であり、一部のプログラムコードが省略されています。
まずは TUtauData
- int main(int oArgC, char* oArgV[])
- {
- // パイプファイルの内容全体を管理するクラス
- TUtauData aUtauData;
-
- // パイプファイルの内容を読み込む
- if ( aUtauData.SetTo(oArgV[1]) != B_OK ) {
- echo("パイプファイルを読み込めません。");
- return -1;
- }
-
Lib UTAU の中心となるクラスは TUtauData であり、UTAU 本体から受け取るパイプファイルの内容をすべて管理するクラスです。
Lib UTAU を利用する場合、まずは TUtauData のインスタンスを生成するところから始めます(4 行目)。
続いて、SetTo() 関数でパイプファイルの内容をすべて読み込みます(7 行目)。UTAU 本体からプラグインへ、パイプファイル名がコマンドライン引数として渡されますので、そのファイル名を SetTo() 関数に渡します。
Lib UTAU 全体を通じて言えることですが、文字列は全て、マルチバイト文字(char)です。ワイド文字(wchar_t)ではありません。ファイル名もマルチバイト文字で渡して下さい。
SetTo() 関数は正常にパイプファイルを読み込めると、定数 B_OK を返してくるので、B_OK 以外が返ってきた場合はエラーとしてプログラムを終了します。
[#SETTING] セクションの管理
- // [#SETTING] セクションの内容を管理するクラス
- const TUtauSectionSettings* aSettings;
- aSettings = aUtauData.SectionSettings();
-
- // [#SETTING] セクションからテンポ(double 型)を取得してみる
- double aTempo;
- if ( aSettings->GetValue(KEY_NAME_TEMPO, &aTempo) == B_OK ) {
- echo("テンポを取得できました:"+num_to_string(aTempo));
- } else {
- echo("テンポが設定されていません。");
- }
-
最初に、パイプファイルの [#SETTING] セクションの情報を取得してみましょう。
[#SETTING] セクションを管理するクラスは TUtauSectionSettings です。TUtauData::SectionSettings() 関数で、TUtauSectionSettings へのポインタを取得できます(3 行目)。
[#SETTING] セクションはリードオンリーなので、TUtauSectionSettings のポインタ変数を宣言する際は const 付きにしておいて下さい。
[#SETTING] セクションに記録されている情報を取得するには、GetValue() 関数を使います(7 行目)。
GetValue() の第 1 引数は、取得したい情報の名称です。テンポを取得する時は KEY_NAME_TEMPO を渡します。KEY_NAME_TEMPO は Lib UTAU で定義されている定数で、実体は "Tempo" です。パイプファイルの中身は「Tempo=120.00」などとなっていますが、ここの名称をそのまま渡せば情報を取得できるということです。
GetValue() の第 2 引数は、取得した値の格納場所です。テンポは小数なので、double 型変数へのポインタを渡すことで、この変数にテンポが格納されます。
8 行目では、取得したテンポを表示しています。echo() 関数は Lib UTAU の関数ではなく、サンプルプログラムで定義している関数です。通常なら cout で良いのですが、ライブラリ作者の環境ですと、cout だと UTAU から起動された場合に値が表示されなかったので、echo() 関数内でメッセージボックスを表示しています。
num_to_string() 関数は Lib UTAU のサポート関数で、数値を文字列(string 型)に変換します。boost が使える環境でしたら、lexical_cast<string> 使えばいいだけなのですけどね。
音符セクションの管理
- // 音符セクション([#0001] など)の内容を管理するクラス
- TUtauSectionNote* aNote;
-
- // UTAU で選択されている音符の歌詞(string 型)をすべて取得してみる
- string aLyric;
- string aAllLyric;
- for ( int32 i = aUtauData.SectionNotesNormalBegin() ; i < aUtauData.SectionNotesNormalEnd() ; i++ ) {
- aNote = aUtauData.SectionNoteAt(i);
- if ( aNote->GetValue(KEY_NAME_LYRIC, &aLyric) == B_OK ) {
- aAllLyric += aLyric;
- } else {
- echo("インデックス "+num_to_string(i)+" の歌詞が設定されていません。");
- }
- }
- echo("歌詞:"+aAllLyric);
-
それでは、パイプファイルの音符セクション([#0001] など)の情報を取得してみましょう。UTAU 本体で選択されているすべての音符の歌詞を繋げてみます。
音符セクションを管理するクラスは TUtauSectionNote です。パイプファイルにはたくさんの音符セクションが並んでいますが、TUtauData::SectionNoteAt() 関数で、TUtauSectionNote へのポインタを取得できます(8 行目)。引数は、何番目の音符セクションかを示すインデックスで、0 から始まります。つまり、SectionNoteAt(0) で最初の音符セクションを取得できます。
したがって、SectionNoteAt(0)、SectionNoteAt(1)……SectionNoteAt(セクションの数-1) というように順に音符セクションを取得していけば、すべての音符セクションにアクセスできます。
ここで注意が必要なのは、[#PREV] セクションの存在です。ご承知の通り、[#PREV] は選択されている音符の前の音符を示します。
SectionNoteAt(0) が返す値は [#PREV] の可能性があります。[#PREV] セクションが不要な場合は、SectionNotesNormalBegin() 関数の出番です(7 行目)。この関数は、[#PREV] セクションを除いた最初のセクションのインデックスを返します。したがって、SectionNoteAt(SectionNotesNormalBegin()) で、[#PREV] セクションを除いた最初の音符セクションを取得することができます。
同様のことが末尾の [#NEXT] セクションについても言えます。そして、SectionNotesNormalEnd() 関数が、[#NEXT] セクションを除いたインデックスの最大値です。
SectionNotesNormalEnd() は STL の end() と同様に有効値の次の値を返すことに注意して下さい。つまり、SectionNoteAt(SectionNotesNormalEnd()) は不正です。SectionNoteAt(SectionNotesNormalEnd()-1) が末尾の音符セクションです。
さて、無事に先頭と末尾のインデックスが分かったので、7~14 行目のループに入ります。
ループの中で各音符の歌詞を取得していますが、歌詞の取得は GetValue() 関数です。
[#SETTING] セクションでテンポを取得した時も GetValue() 関数を使いました。関数名が同じなのは偶然でも何でもなく、実は、TUtauSectionSettings も TUtauSectionNote も、同じ TUtauSection クラスから派生しています。GetValue() 関数は TUtauSection クラスの関数なのです。
したがって、テンポの取得も歌詞の取得もやり方は変わりませんので、解説は省略します。テンポの時と違うのは、歌詞は文字列なので、第 2 引数に string 型へのポインタを渡しています。
パイプファイルのバージョン間の相違を吸収
- // パイプファイルのバージョンを管理するクラス
- const TUtauSectionVersion* aVersion;
- aVersion = aUtauData.SectionVersion();
-
- // 最初の音符のモジュレーション(int32 型)を取得してみる
- // UTAU 0.2.90(キー名は "Modulation")でも UTAU 0.2.75(キー名は "Moduration")でも取得できるようにする
- int32 aMod;
- aNote = aUtauData.SectionNoteAt(aUtauData.SectionNotesNormalBegin());
- if ( aNote->GetValue(aVersion->CompatibilityKey(KEY_NAME_MODULATION), &aMod) == B_OK ) {
- echo("モジュレーションを取得できました:"+num_to_string(aMod));
- } else {
- echo("モジュレーションが設定されていません。");
- }
-
次に、[#VERSION] セクションの管理について解説します。
[#VERSION] セクションを管理するクラスは TUtauSectionVersion です。TUtauData::SectionVersion() 関数で、TUtauSectionVersion へのポインタを取得できます(3 行目)。
[#VERSION] セクションはリードオンリーなので(UTAU の仕様上リードオンリーなのかは不明ですが、Lib UTAU ではリードオンリーとして扱っています)、TUtauSectionVersion のポインタ変数を宣言する際は const 付きにしておいて下さい。
さて、UTAU のバージョン(パイプファイルのバージョン)によって、パイプファイルの記述が少しずつ変わってきます。差異を考慮してプログラムを組むのは大変ですが、TUtauSectionVersion は、差異吸収作業を手助けする機能を持っています。
それが CompatibilityKey() 関数です。CompatibilityKey() は、最新の UTAU でのキー名を渡すと、現在読み込んでいるパイプファイルのバージョンに合わせたキー名を返してくれます。
例えば、モジュレーションは、UTAU 0.2.90 では "Modulation" ですが、UTAU 0.2.75 では "Moduration" です(l と r の違い)。
CompatibilityKey() に最新の "Modulation" を表す KEY_NAME_MODULATION を渡し、CompatibilityKey(KEY_NAME_MODULATION) とすると、UTAU 0.2.90 のパイプファイルを読み込んでいる場合は KEY_NAME_MODULATION が返り、UTAU 0.2.75 のパイプファイルを読み込んでいる場合は KEY_NAME_MODURATION が返ります。
つまり、音符セクションからモジュレーションを取得する際、GetValue(aVersion->CompatibilityKey(KEY_NAME_MODULATION), &aMod) とすることで、UTAU 0.2.90 だろうと UTAU 0.2.75 だろうと気にせず、モジュレーションの値を取得することができます(9 行目)。
なお、モジュレーションは整数なので、GetValue() の第 2 引数には int32 型へのポインタを渡しています。int32 は要するに long です。short や long だと型の大きさが直感的に分かりづらいので、Lib UTAU では int8、int16、int32 というような表記を用いています。
実のところ、現在、CompatibilityKey() は KEY_NAME_MODULATION を変換する機能しかありません。なんとなく、他にもバージョン間で差異のあるプロパティはある気がしていますが、いまいち把握できていないので、実装できていません。コメント投稿ページで差異を具体的に教えていただければ(UTAU のバージョンとプロパティ名など)、対応を検討させていただきます(必ずしも対応できるとは限りません)。
値の設定
- // UTAU で選択されている音符の音の強さ(int32 型)をすべて半分にする
- int32 aIntensity;
- for ( int32 i = aUtauData.SectionNotesNormalBegin() ; i < aUtauData.SectionNotesNormalEnd() ; i++ ) {
- aNote = aUtauData.SectionNoteAt(i);
- if ( aNote->GetValue(KEY_NAME_INTENSITY, &aIntensity) == B_OK ) {
- if ( aNote->SetValue(KEY_NAME_INTENSITY, aIntensity/2) != B_OK ) {
- echo("インデックス "+num_to_string(i)+" の音の強さを設定できませんでした。");
- }
- } else {
- echo("インデックス "+num_to_string(i)+" の音の強さが設定されていません。");
- }
- }
- echo("音の強さを半分にしました。");
-
これまで値の取得について見てきましたが、今度は、値の設定について解説します。UTAU で選択されている音符の音の強さを、すべて現在の半分に設定してみます。
3~12 行目のループの回し方は、全ての音符の歌詞を取得した時と同じやり方です。
5 行目で、音符の音の強さ(KEY_NAME_INTENSITY)を取得し、int32 型の変数 aIntensity に格納しています。
6 行目が音の強さを設定している箇所です。値の設定には SetValue() 関数を使います。GetValue() 関数と使い方はほとんど同じで、第 1 引数がキー名です。音の強さを設定したいので KEY_NAME_INTENSITY を渡しています。第 2 引数が設定したい値で、現在の半分にしたいので、先ほど取得した aIntensity を 2 で割って渡しています。GetValue() と異なり、SetValue() では第 2 引数は値渡しです。
パイプファイルへの書き戻し
- // 変更内容をパイプファイルに書き戻す
- if ( aUtauData.Export(oArgV[1]) == B_OK ) {
- echo("パイプファイルに変更内容を保存しました。");
- } else {
- echo("パイプファイルに変更内容を保存できませんでした。");
- }
-
設定した値を UTAU 本体に返すには、パイプファイルを更新します。
パイプファイルに変更点を書き出す関数は、TUtauData::Export() です(2 行目)。Export() 関数は、TUtauData の全てを書き出すのでは無く、SetValue() で更新されたプロパティのみ書き出します。
以上で、サンプルプログラムの解説を終わります。
追記予定
- 先行発声やオーバーラップも考慮し、さらに食い込み補正も計算に入れた実質の音符の長さを算出する TUtauSectionNote::GetCorrectedDuration()
- 食い込み補正を計算に入れた値を取得する TUtauSectionNote::GetCorrectedPreUtterance()、GetCorrectedVoiceOverlap()
- 「10,10,10,20,30,50,60,%,10,10,40」というエンベロープの表記を「(10, 20) (20, 30) (30, 40) (475, 50) (485, 60)」というように分かりやすく解釈する TUtauSectionNote::GetCorrectedEnvelope()
- PBS、PBW、PBY に散らばっているピッチベンドをまとめる TUtauSectionNote::GetCorrectedPitch()