●ISPR (ISPR-V3.0) 出力プラグインのできるまで

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 AviUtl には、入力プラグイン、出力プラグイン、フィルタプラグインの
 3種類のプラグインがあります。

 今回の目的は ISD ファイルを作成することですので、出力プラグインを
 作ることにしました。

 ispr規格にも様々なフォーマットがありますが、ここは単純に、非圧縮で
 ADPCM の ISPR-V3.0 固定で行きます。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●AviUtl plugin programing

 AviUtl の PluginSDK は C で記述されていますので、VC++ 6.0 程度の
 開発環境が必要です。

 まず、最初に行ったのはサンプルPlugin『連番BMP出力』の確認です。
 ISPR-V3.0 は非圧縮なので、これが構成的に最も近いと考えたのです。

 始める前は、画像や音声がどのように渡されてくるのか心配でしたが
 嬉しいことに AviUtl は画像も音声も圧縮なしの展開済生イメージで
 提供してくれます。

 更に音声データに至っては、全範囲から指定した範囲だけを切り出して
 渡してくれますし、詳細情報は oip で示される構造体に入ってくる等、
 至れり尽くせり。

 以下に、ソースの一部抜粋を示しますが、何となく簡単そうでしょ?

  // 処理するフレーム数だけループする
  for( i = 0; i < oip->n; i++ ) {

    // 中断検知
    if( oip->func_is_abort() ) break;

    // 残り時間の表示
    oip->func_rest_time_disp(i,oip->n);

    // DIB形式で24bitの画像を得る
    pixelp = (unsigned char *)oip->func_get_video_ex(i,NULL);

    // 音声データがある場合は WAVデータを読み出す
    if ( oip->flag & OUTPUT_INFO_FLAG_AUDIO ) {

      // short 配列として WAVデータを得る
      audiop = (short *)oip->func_get_audio( sp_fir, cnt_fir, &audio_readed );

      // ***ここで、得た WAV データから ADPCM データを作成***
    }

    // ***ここで、ISD 形式の変換と出力を行えば良い***

    // プレビュー画面の表示
    oip->func_update_preview();
  }

 プラグイン側では、単に与えられたフレーム数だけの処理をループで
 処理すれば良いので、特に面倒なことはありません。

 雛形どおりに、キャンセル処理、画面表示の更新処理を呼び出せば、
 それで良いのです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●ISD出力のための画像変換処理

 画像については、1フレーム毎に 24bit DIB Bitmap から、X680x0
 GVRAM 形式 2byte へ変換すれば良いだけなので、簡単な処理です。

 isprヘッダの作成と出力は、フレーム処理ループの前に済ませて
 おけば OK ですね。

 先に書いたように DIB 24bit 形式で画像が得られるので、これを
 どうにか処理して 1pixel = 2byte に変換するのですが…今時の
 Windows機で動かすのですから、メモリにも CPU速度にも十二分な
 余裕がありますので、AVI2ISD.x ver.0.04 の持っていた 4種類に
 加え、更に 2種類の誤差拡散処理を追加してみました。

 (計算が速いと、アイディアを気楽に組み込めるから良いです)

  [組み込んだ減色処理の一覧]

   ○bit切捨      : 8bitデータの下位3bitを落とし5bitにします
   ○2x2ディザ     : 2x2パターンのディザを適用します
   ○4x4ディザ     : 4x4パターンのディザを適用します
   ●2x2時間軸ディザ  : フレーム毎に異なる2x2ディザを適用します
   ○時間軸誤差拡散  : フレーム間でピクセル誤差を拡散します(砂嵐度高)
   ○平面誤差拡散   : フレーム内で誤差拡散を行います

 この他、AVI2ISD.x の時と同様にガンマ補正機能も組み込み、
 行間を空ける/空けないの指定(ISDBLANK相当)、縦横独立の
 センタリング(SETPOS相当)も装備しました。

 輝度bit の扱いについては最後まで悩みましたが、例えばモノクロ
 動画だった場合には 32階調→64階調 と大幅に貢献できることから
 全ての処理で輝度bit の利用可否を選択できるようにしています。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●ISD出力のための音声変換処理

 音声変換処理は AVI2ISD.x ではやってなかった部分なので全て
 新規に起こしています。基本的には次の流れになります。

  1. AviUtl のコールで必要な範囲の音声データを得る
  2. ステレオならばモノラル化する
  3. サンプリング周波数を 15.625 kHz に変換する
  4. PCM -> ADPCM 変換を行う

 モノラル化は単純に (Left + Right) / 2 で処理すれば良いのですが、
 厄介なのが 3. のサンプリング周波数変換と、4. の ADPCM 変換です。

 通常、サンプリング周波数変換は、src と dst の最小公倍数となる
 周波数までアップ・サンプリング(データの間に 0 を埋め込む)し、
 そこから目的の周波数へとダウン・サンプリング(データ間引き)を
 行います。

 しかし・・・普通は 44.1kHz とか 48.0kHz のデータが主であって、
 15.625kHz なんて中途半端なものはありません。

 実際、これらのメジャーなサンプリング周波数と 15.625kHz の最小
 公倍数を求めると、あまりにも大きくなりすぎて、計算量が膨大に
 なってしまいます。

 そこで、アップ・サンプリングはせずに dst / src となるように、
 ブレゼンハムアルゴリズムの手法でデータを間引くことにしました。

 そのまま間引いたのでは、折り返しノイズもあって満足に聞ける
 音にはなりません(ぶちぶち言って満足に聞けない音です)ので、
 2. と 3. の間に、カットオフ周波数 15.625 kHz のローパス・
 フィルタを挿入する事で、これを回避しています。

 PCM -> ADPCM 変換については、OKI ADPCM の詳細情報が公開されて
 いない事から不明な部分が多々ありますが、実用上問題ない程度の
 変換方法が NetBSD のソース (msm6258.c) にありますので、これを
 参考にして実装しています。

 こうして、最終的には次の流れで 44.1kHz, 48.0kHz の音声データを
 15.625kHz の ADPCM データに変換できるようになりました。

  1. AviUtl のコールで必要な範囲の音声データを得る
  2. ステレオならばモノラル化する
  3. ローパスフィルタをかける(FIRデジタルフィルタ)
  3. サンプリング周波数を、間引きで 15.625 kHz に変換する
  4. PCM -> ADPCM 変換を行う

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●その他

 画像と音声の処理ができてしまえば、あとは細かいところの微調整と、
 減色方法やガンマ補正、センタリング指定等を自由に変更できるように
 ダイアログを作成すれば完了です。

 AviUtl Plugin は、それぞれに独自の設定ダイアログを持てるように
 なっており、サンプルのダイアログもありますので、こちらも簡単に
 作成できます。本当に、AviUtl は良く作られてますねぇ。

 こうして、ISD出力プラグイン ispr_output.auo が完成しました。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●参考文献など

 NetBSD ソース (msm6258.c)

 AviUtl PluginSDK
  AviUtlのお部屋
  http://spring-fragrance.mints.ne.jp/aviutl/

(EOF)