VEGASスクリプトで字幕を操作したくなったら、結局Formを使うことになった件

# はじめに。

こん××は。
前回、VEGASのスクリプトの雛形を作った記事を記載いたしました。

https://qiita.com/cyross4vocaloid/items/7c8ad8247836d34d2c22

実は、これをもとにしてスクリプトを作成しており、第１号のスクリプトが出来上がりました。

https://github.com/cyross/VegasInsertAudioFileFromDirectory

フォルダ内にある音声ファイルをオーディオトラックに流し込むスクリプトです。

今回のお題の中心はこちらのスクリプトに関して。

https://github.com/cyross/VegasScriptApplySerifuColor

こちらはトラック中にある字幕から声優名を取り出して、対応する色を当てはめるものです。
字幕データを編集しようということでやってみたところ、
色々試行錯誤したお話。

# 字幕データを引っ張り出してみたら…

他の方の記事にもあるのですが、VEGASのスクリプトの仕様は非常に不親切で、頭の中で考えるより実際に手を動かして試行錯誤したほうが手っ取り早いのはたしかです。

というのも、VEGASで字幕はどうやって実現しているのかというと…。

**エフェクトなのです。**

しかも、

**OFXなのです。**

ちょっと目がクラクラしてきました。果たして、無事に取得できるのかと…。

試行錯誤の結果、なんとかして字幕のデータを取得することが出来ました。
簡単なコードで書くとこんな感じです。

```C#
using System.Linq;
using ScriptPortal.Vegas;

public partial class VegasScriptSample
{
    // 実際では、OFXStringParameterまでの取得にしたほうが
    // 利便性が大きいが、本記事での便宜上、文字列まで取得している。
    public string[] GetJimakuList(Vegas vegas)
    {
        // 選択しているトラックを抽出
        Track selected = SelectedTrack(vegas.Project);
        if(selected is null){ return; }

        if(not track.IsVideo()){ return; } // 対象がビデオトラックのみ

        Media[] mediaList = GetMediaListFromEvents(track.Events);
        OFXStringParameter[] params =  GetStringParameters(mediaList);
        return GetStringsFromStringParameters(params);
    }

    // 選択しているトラックを取得
    public Track SelectedTrack(Project project)
    {
        foreach(Track track in project.Tracks)
        {
            if (track.Selected)
            {
                return track;
            }
        }
        return null;
    }

    // トラック内では、字幕などは「イベント」という単位で設置されているので
    // そこから、字幕を取り扱うオブジェクト・ジェネレートメディア（Media）を取り出す
    // テイクは一つのみが前提
    private Media[] GetMediaListFromEvents(TrackEvents events)
    {
        IEnumerable<Media> mediaList = events.Select(e => e.Takes[0].Media);
        return mediaList.ToArray();
    }

    // 字幕はOFXのパラメータとして保存されているので、そのデータを取り出す
    private OFXStringParameter[] GetStringParameters(Media[] mediaList)
    {
        return mediaList.Select(m => _GetStringParameter(m)).ToList().ToArray();    }

    // 字幕はOFXStringParameterクラスのオブジェクトなので、それを取り出す
    private OFXStringParameter _GetStringParameter(Media media)
    {
        foreach(OFXParameter param in media.Generator.OFXEffect.Parameters)
        {
            if(param.ParameterType == OFXParameterType.String)
            {
                return (OFXStringParameter)param;
            }
        }
        return null;
    }

    private string[] GetStringsFromStringParameters(OFXStringParameter[] parameters)
    {
        return parameters.Select(p => GetStringFromOFXParameter(p)).ToArray();
    }

    // OFXStringParameterから文字列を取り出す
    // 文字列自体は時間単位で管理されているが、時間単位で
    // 文字列の情報が変わっていない前提
    private string GetStringFromOFXParameter(OFXStringParameter param)
    {
        return param.GetValueAtTime(new BaseTimecode());
    }
}
```

…めんどくさいですね。

とりあえず、ようやく文字列を取得できました…が、ここから先でも苦労しました。

**字幕データがRTFだったのです…**

# RTF処理までの試行錯誤の結果

もうどうやってやろうかと…。できる限りFormを使わずに処理することが出来ないか…。そんなことを考えていましたが、結局`System.Windows.Forms.RitchTextBox`クラスを使うことにしました。

```C#
using System.Windows.Forms;
using ScriptPortal.Vegas;

public partial class VegasScriptSample
{
    private readonly RichTextBox box = new RichTextBox();

    // 1. RTF中の":"より前の文字列を取得
    // 2. ":"までの文字列を削除
    public string GetPrefixFromRtfWithCut(string rtf)
    {
        box.Rtf = rtf
        int pos = box.Find(":");
        if (pos == -1)
        {
            return null;
        }
        string actor_name = box.Text.Substring(0, pos)s;
        box.Select(0, pos + 1);
        box.Cut();
        box.Focus(); // たまに編集が反映されない時があるのでこれで明示的に反映
        return actor_name;
    }
}
```

こんな感じで文字列を抜き出したり、抜き出した部分を削除することができます。

# おわりに

あと、文字列の色を変える際は、RTF中のフォントカラーを変更するのではなく、OFXのパラメータを変更する必要があるのですが、これはまた別の機会に…。


# おまけ

現在作っているスクリプトから、プロジェクトテンプレートとして使えるようにした雛形もリポジトリにアップしております。

https://github.com/cyross/VegasScriptPrescribedPattern