チュートリアル

Rubyで動画を造ろう

2014/02/06

実験のために大量の単純な動画が必要になった。Flashなどで一つ一つ丁寧に作るという手はもちろんあった。今考えてみればそれほど時間はかからなかったかも知れないが、最近はもっぱらプログラミングに没頭する日々が多いせいかそういう単純労働をやる気になれない。やれやれ。どうしよう。多少恥ずかしい気分に襲われた。昔、僕ってMaxの名人と言われたことがなかったっけ?MaxとJitterなら出来ると言えば出来る。Maxを使いたいかというと、ふむ、まあ、特に使いたいと思わないのが本音だ。コードを書きたい。いや、コードを書くのが今の自分にとって自然なんだ。

ということで、Rubyでモーショングラッフィクス的な動画が作れないかと挑戦してみた。

この挑戦にとりあえず必要なツールは2つ。まず、描画はRCairoで行う。RCairoはCairoライブラリーのRubyバインディングだ。CairoはIllustratorなどのようにベクトル・グラフィックが描けるかなりパワフルなライブラリーだが、Rubyist Magazineにて日本語の紹介記事が読める。RCairoを使えば、静止画が簡単に書き出せるが動画は無理だから、RCairoで描いた絵をなんとか動画に変換する必要がある。いろいろ試した結果、結局短い動画ならフレームを1つ1つ画像ファイルに出力し、FFmpegでその画像たちを動画に仕上げるという戦略を選んだ。一見面倒な手法に見えるかも知れない。でも意外と楽で、画像を書き出した後、FFmpegでいろいろと圧縮設定を変えたりして動画を手軽に書き直すことができる。

では、まずFFmpegを入手する。便利なツールだからこんなプロジェクト以外にも役立つ。入手方法はいろいろあるが、OSXならHomebrewを使った方がおすすめ。

Homebrewそのもののインストール。ターミナルで以下のコマンドを実行する:

ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"

そしてHomebrewの初期処理を行う:

brew doctor

FFmpegをインストールする:

brew install ffmpeg

最後にRCairoもインストールする:

gem install cairo

それで下準備ができた。早速コードを書こう。とりあえず簡単な絵の画像を書き出してみよう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'cairo'
 
width = 1280
height = 720
pixel_format = Cairo::FORMAT_ARGB32
 
surface = Cairo::ImageSurface.new(pixel_format, width, height)
context = Cairo::Context.new(surface)
 
context.set_source_rgb(0,0.1,0.333)
context.rectangle(0,0,width,height)
context.fill()
 
surface.write_to_png("test.png")

上記のスクリプトを実行すれば… おおおおお!こんな奇麗が画像が出来上がった。

test

すばらしい。すばらしい。でも大したことはやっていない。Cairoで描画する前にサーフェイス(面)とコンテキストが必要。絵画で例えるとサーフェイスがキャンバスでコンテキストが筆だ。基本的にコンテキストを通して描画を行う。行7でサーフェイスを作る。その際、ピクセルのフォーマットと画像の幅と高さを指定する。ここで使うピクセルフォーマットは32ビットのRGBA(各チャンネルは8ビット)。

次はいよいよ描画だ。Contextのset_source_rgbメソッドで色を指定する。アルファチャンネルを使用するならset_source_rgbaを使う。ここは奇麗なブルーにする。次の行は長方形を描くように見える。しかし、長方形が描かれるのは次の行のcontext.fill()だ。そう。Illustratorを使っている人なら基礎中の基礎だが、ベクトル絵には「塗り」と「線」がある。英語では塗りのことを「fill」といい、線は「stroke」だ。つまり、ここで長方形を塗るという指示を出している。最後にサーフェイスのwrite_to_pngメソッドでPNGファイルを出力する。

では、何かを動かそう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require 'cairo'
 
width = 1280
height = 720
pixel_format = Cairo::FORMAT_ARGB32
 
surface = Cairo::ImageSurface.new(pixel_format, width, height)
context = Cairo::Context.new(surface)
 
15.times do |frame_no|
	y = height * 0.5 + height * 0.4 * Math::sin(2 * Math::PI * frame_no.to_f / 15.0)
 
	context.set_source_rgb(0,0.1,0.333)
	context.rectangle(0,0,width,height)
	context.fill()
 
	context.set_source_rgb(1,1,1)
	context.arc(width * 0.5, y, 35, 0, 2 * Math::PI)
	context.fill()
 
	Dir.mkdir("cache") unless File.exists?("cache")
	surface.write_to_png("cache/img#{frame_no}.png")
end

ちょっと長くなったが、最初のプログラムと大して変わったことはやっていない。大きな違いは1枚ではなく15枚もの画像を出力していることだ。そして、下記のコードで白い正円を描いている。

context.set_source_rgb(1,1,1)
context.arc(width * 0.5, y, 35, 0, 2 * Math::PI)
context.fill()

context.arcで正円を描く場合、x座標、y座標、半径を渡してからさらに弧の開始角度と終了角度が必要。円なら、0から2πにする。y座標はサイン派に乗って上下する。

y = height * 0.5 + height * 0.4 * Math::sin(2 * Math::PI * frame_no.to_f / 15.0)

「cache」フォルダーがなければ作ってから15枚の画像をそこに保存する。

Dir.mkdir("cache") unless File.exists?("cache")
surface.write_to_png("cache/img#{frame_no}.png")

後一歩。

FFmpegを使えば、その画像から動画が簡単に作れる。一旦Rubyをやめてターミナルに戻る。

ffmpeg -r 29.97 -i cache/img%d.png -vcodec mpeg4 -b:v 2M ball.mp4

「cache」フォルダーにあるimg<フレーム番号>.pngという形式のファイル名を持つ画像を入力にする。しかし、画像にはフレームレートがないので、読み込み前に「-r 29.97」でフレームレートを指定する。出力のフォーマットを「mpeg4」にして、「-b:v 2M」でビットレートを2 Mbpsにして「ball.mp4」に動画を保存する。こんな感じになる(ブラウザーによって再生できない場合がある):

いいじゃないか。でもターミナルに戻るのはやはり面倒だ。運良く、Rubyで上記のコマンドが簡単に実行できる。おまけに、途中に生成したファイルを削除することもできる。でも冒頭に書いた通り、残すとターミナルで圧縮設定を変えて動画を書き直すことができる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require 'cairo'
 
width = 1280
height = 720
pixel_format = Cairo::FORMAT_ARGB32
 
surface = Cairo::ImageSurface.new(pixel_format, width, height)
context = Cairo::Context.new(surface)
 
15.times do |frame_no|
	y = height * 0.5 + height * 0.4 * Math::sin(2 * Math::PI * frame_no.to_f / 15.0)
 
	context.set_source_rgb(0,0.1,0.333)
	context.rectangle(0,0,width,height)
	context.fill()
 
	context.set_source_rgb(1,1,1)
	context.arc(width * 0.5, y, 35, 0, 2 * Math::PI)
	context.fill()
 
	Dir.mkdir("cache") unless File.exists?("cache")
	surface.write_to_png("cache/img#{frame_no}.png")
end
 
`ffmpeg -r 29.97 -i cache/img%d.png -vcodec mpeg4 -b:v 2M ball.mp4`
`rm -rf cache`

究極につまらない動画だが、同じ方法で究極に格好いいジェネレーティブ・アートも造れるので、一度試してみてください。

DMX 512の基礎

2008/06/17

「DMX 512」、通称「DMX」は照明や舞台効果を制御する為の通信プロトコルです。制御信号を発信するコントローラとその信号に反応する機器(デバイス)の通信方式を定める規格です。

DMX 512の歴史

本来、照明効果の制御はアナログ信号によって行われていました。1つの照明器具にケーブルが1本必要だった為、配線がすぐに複雑になっていました。AMX192やD54と言ったアナログ規格がその問題を解決する為に登場しましたが、デジタル通信であれば複数の制御信号を同じケーブルで送る事が出来るだけでなく、コンピュータなどによる制御も容易になります。そのデジタル通信方式を定める規格、DMX512が1986年に誕生し、1990に規格が改訂され(DMX512/1990)、2004年にANSI規格(DMX512-A)となりました。しかし、DMX512規格の幾つかの規定 が多くの照明機器メーカーに無視されたり、適当に解釈されたり、実装に不統一が多いのが現状です。

DMX 512の概念

DMX 512規格は単純且つ丈夫なシステムを目指して作られました。照明器具は最低限なデータ処理だけを行えば、電子基盤が単純になってコストが抑えられ、安定性も高くなります。また、過酷な環境での動作も想定されていたので丈夫さも重視されました。

接続

DMX 512は制御信号を発信するコントローラとその信号に反応する機器があります。コミュニケーションは一方的でコントローラ以外の機器は自ら制御信号を発信する事はありません。

コントローラと機器はデイジーチェーン(数珠繋ぎ)方式で接続されます。直列に接続されていくDMX 512機器は原則としてDMXイン(入力)とDMXアウト(出力)の端子を搭載しています。入力されたデータは次の機器の為にそのまま出力されます。デイジーチェーンの最後に接続された機器はそのチェーンを閉じなければなりません。次に何もないのを認識し、内部で自動的にチェーンを閉じる機器もありますが、一般的DMXターミネーターという道具を最後の機器の出力端子に挿してチェーンを閉じます。ターミネーターを使わなければ通信エラーの原因になります。

DMXのデイジーチェーンの一例

DMXターミネーター

コネクタとケーブル

DMX 512の規格ではキャノン5ピンのコネクタの使用が規定されます。しかし、これが最もよく無視される規定です。キャノン3ピンのコネクタもよく見られますし、珍しくホーン・コネクタを搭載する器具もあります。5ピンを3ピンに変換するアダプターを使えば、異なる端子を持つ機器を同じデイジーチェーンに接続する事が出来ます。さらに、規格に反してピンの極性を逆にする機器もあります。その為、極性(polarity)が変えられるコントローラがあります。しかし、極性の異なる機器は同じデイジーチェーンに接続する事は出来ません。


キャノン5ピン→3ピンの変換アダプター

キャノンの3ピン・コネクタはアナログ・オーディオで頻繁に使われます。コネクタが全く同じですのでオーディオ・ケーブルをDMX端子に挿す事は物理的に可能です。しかし、オーディオ・ケーブルはDMX 512に不適合です。DMX 512規格はケーブルのインピーダンスを120Ωに定めます。インピーダンスとは電子信号に対する抵抗で、ケーブルと電子回路などのインピーダンスが異なれば(インピーダンス不整合)、デジタル信号にエラーが発生しやすくなります。アナログ・オーディオはインピーダンス不整合の影響を受けにくいのでオーディオ・ケーブルのインピーダンスが表示される事は殆どありません。おおよそ50Ωが一般的で、DMX 512の120Ωとの差が大きいのです。ネットワーク・ケーブルとして広く使われるCat 5ケーブルがDMX 512信号に適合しているかどうか1998年に実験が行われました。通信に全く問題が見られなかったとの結果で、キャノン・コネクタを付ければCat 5ケーブルが利用できます。また、デジタル・オーディオ通信規格のAES/EBUではキャノン・コネクタの110Ωのケーブルが用いられるのでDMX 512ケーブルがなければAES/EBUケーブルで代用できるでしょう。

DMXの3ピンキャノン端子とアナログ・オーディオのキャノン端子:入出力のオス・メスの割当が逆

電子信号が異なったインピーダンスのケーブルや回路を通過すると水面の波紋がプールの壁にぶつかるように一部が返されます。その返された信号が通信エラーの原因です。アナログのオーディオ信号は波長が非常に長い(数キロ)のでインピーダンスの不整合による反射の影響が少ないのです。

データ形式

DMX 512のデータ・プロトコルは極めて単純です。コントローラが発信するパケットの単位はスロットと言います。1スロット=8ビット。パケットの最初のスロットはスタート・コードと言い、データの種類を指定します。残りはデータ・スロットです。データ・スロットの事をチャンネルとも言います。チャンネルは24〜512送信できますが、使われなくても全512チャンネルを送信するコントローラが多いです。

デイジーチェーンのそれぞれの機器にスタート・アドレスを指定します。パケットを受信すればデータ・スロットを数えていきます。自分のスタート・アドレスにあたるチャンネルに反応します。単純な機器は1チャンネルで制御できますが複数のチャンネルを要する機器もあります。例えば、スタート・アドレスが12で6チャンネルを要する機器は12番〜17番目のスロットに反応します。

機器のスタートアドレスはディップスイッチやLEDメニューで指定します

受信されたデータにどう言う風に反応するか機器によって違います。ライトの強弱を調整するディマーなら1チャンネルが1つのライトの明るさを指定します。ゴボ(投影される模様)が交換できる機器は各ゴボに番号を与えてDMXデータの値によってゴボを変えます。

データは一方的にコントローラから発信される為、問題なく受信されたかどうか確認できません。その為に花火など危険な舞台効果の制御に使用してはいけません。

参考

日本語:
DMX512について
DMX 512-A
DMX512-A – Wikipedia

英語:
DMX 512 FAQ
Report on DMX 512 Over CAT 5 Cable.
Wikipedia: DMX 512-A