ついにベールを脱いだJavaFX

第10回サウンドとムービー、そしてProduction Suite

最近のGUIでは、サウンドやムービーは欠かせない要素になってきています。特にYouTubeが登場してからは、ムービーが当たり前に扱われるようになっています。しかし、Javaではサウンドやムービーなどのメディアは苦手としてきた領域です。

JavaFXではこの状況を打開すべく、JavaOne 2008にてOn2 Technologyとの提携が発表されました。On2 Technologyといえば、Adobe Flash Videoが採用しているVP6コーデックを開発してる企業です。この提携によりJavaFXでもAdobe Flash VideoのFLVフォーマットを再生できるようになりました。

また、同じくJavaが苦手としている領域として、デザイナーとの分業があります。デザイナーでも扱えるツールでGUIを構築することがなかなかできないというのが、Javaの現状です。これはJavaFXでも同じです。

この問題に対し、JavaFXでは既存のツールを活かすという方法を採用しました。それがJavaFX Production Suiteです。

Production Suiteにはいくつかのツールが含まれていますが、キーとなるのがAdobe IllustratorとAdobe Photoshopのプラグインです。IllustratorとPhotoshopはデザイナーにとってデファクトスタンダードとなりつつあるツールです。Production Suiteのプラグインを使用することで、IllustratorやPhotoshopで作成したアートワークをそのままJavaFXの世界に持ち込むことができるのです。

今回は、この2つの領域、メディアとProduction Suiteについて解説していきます。なお、今回使用したサンプルのソースを含めたNetBeansのプロジェクトは、下記のリンクよりダウンロードすることができます。

JavaFXがサポートしているメディア

サウンドやムービーのAPIを紹介する前に、現在JavaFXでサポートしているメディアについてまとめておきましょう。現状では、JavaFXを動作させるプラットフォームによってサポートしているメディアが異なります。表1にサポートしているメディアをまとめました。

表1 JavaFXがサポートしているメディアJavaFX FAQより引用)
プラットフォーム コーデック ファイルフォーマット
Mac OS X 10.4以降 ビデオ: H.261、H.263、H.264、MPEG-1、MPEG-2、MPEG-4、Sorenson Video 2、Sorenson Video 3
オーディオ: AIFF、MP3、WAV、MPEG-4 AAC Audio、MIDI
.3gp、.3gpp、3g2、3gp2、
.avi、.mov、.mp4、.mp3、
.m4a、.m4b、.m4p
Windows XP、Vista ビデオ: Windows Media Video、H.264
オーディオ: MPEG-1、MP3、Windows Media Audio、 MIDI
.mp3、.wav、
.wmv、.avi、.asf
Windows XP、Vista、Mac OS X 10.4以降、Linux、Solaris ビデオ: On2 VP6
オーディオ: MP3
.flv、.fxm、.mp3

WindowsではDirectXに含まれるDirectShowがサポートしているメディアを使用することができ、Mac OS XではQuickTimeがサポートしているメディアを使用することができます。

プラットフォームによらずサポートしているサウンドはMP3です。ムービーはコーディックとしてOn2 VP6を使用したAdobe Flash VideoのFLVフォーマット、またSun Microsystems社が策定したFLVのサブセットであるFXMフォーマットを使用することができます。

FLVフォーマットと、FXMフォーマットの両方フォーマットを作成できるツールは、現状On2 Flixしかありません。On2 Flixは試用版が60日間使用できるので、試しに使ってみるのはいいかもしれません。ただし、試用版は左下にOn2のクレジットが入ってしまいます。

FLVフォーマットだけであれば、Adobe Creative Suite 4に含まれるAdobe Media Encoderで作成することができます。本解説でもAdobe Media Encoderを使用してムービーをエンコードしました[1]⁠。

サウンドを扱う

では、実際にメディアに関するAPIを使用していきましょう。まずはサウンドです。

とはいうものの、サウンドもムービーも同じクラスを使用して再生します。サウンドとムービーの違いは描画の有無でしかありません。

メディアを扱うのがjavafx.scene.media.Mediaクラスです。Mediaクラスは再生するメディアをsourceアトリビュートで表します。sourceアトリビュートはURIでメディアの位置を指定します。サウンドの場合、プロトコルとしてhttp:とfile:が使用できます。また、サウンドファイルをJARファイルに含めることも可能です。

メディアを再生するのがjavafx.scene.media.MediaPlayerクラスです。MediaPlayerオブジェクトのmediaアトリビュートにMediaオブジェクトを指定し、play関数をコールすることでメディアを再生します。再生の停止はstop関数、ポーズはpause関数です。たとえば、クラスファイルと同ディレクトリにあるsound.mp3ファイルを再生するには次のようなスクリプトを使用します。

リスト1
var player = MediaPlayer {
     // 再生するサウンドファイル
    media: Media {
        source: "{__DIR__}sound.mp3"
    }
};
 
// サウンドの再生
player.play();

これだけです。簡単ですね。

MediaPlayerクラスにはmediaアトリビュート以外に、ボリュームを表すvolumeアトリビュートや、繰り返して再生する回数を表すrepeatCountアトリビュートなどがあります。これらのアトリビュートも使ってサンプルを作成してみましょう。

音符の形のノードをクリックしている間サウンドを再生し、マウスボタンを放すとサウンドの再生を停止するというサンプルです。それだけでなく、音符をドラッグすることにより、ボリュームや左右のバランスを変更することも行ってみましょう。垂直方向のドラッグがボリューム、水平方向のドラッグがバランスを変更するようしてみました。

音符の形は楕円と直線を用いて作成しました。音符の位置はlocationX変数とlocationY変数にバインドさせます。locationXがバランスに連動し、locationYがボリュームに連動します。

以下にMediaPlayerオブジェクトを生成する部分を示します。

リスト2
// 音符の形の x 座標
// サウンドの左右のバランスに連動
var locationX = 100.0;
 
// 音符の形の y 座標
// サウンドのボリュームに連動
var locationY = 100.0;
 
var player = MediaPlayer {
    // 左右のバランスは -1.0 から 1.0 の範囲
    balance: bind locationX / 100.0 - 1.0
 
    // ボリュームは 0.0 から 1.0 の範囲
    volume: bind (200.0 - locationY) / 200.0
 
    // 無限に繰り返す
    repeatCount: MediaPlayer.REPEAT_FOREVER
 
    // 再生するサウンドファイル
    media: Media {
        source: "{__DIR__}sound.mp3"
    }
}

locationXとlocationYは0から200までの値をとるようにしています。左右のバランスを表すbalanceアトリビュートは-1.0から1.0の値を取るので、locationXと連動できるように100で割り、1を引いています。同様にvolumeは0.0から1.0の値を取り、上にいくほどボリュームを大きくさせるため、200からlocationYを引き、200で割っています。

また、クリックしている間ずっとサウンドを再生させるため、repeatCountアトリビュートを無限に繰り返すMediaPlayer.REPEAT_FOREVERに設定しています。

次に音符の形のノードをドラッグして位置を変化させる部分を示します。

リスト3
Stage {
    title: "Sound Controller"
    resizable: false
     
    scene: Scene {
        width:200
        height: 200
 
        content: [
            // 四分音符の形
            Group {
                translateX: bind locationX
                translateY: bind locationY
 
                content: [ <<省略>> ]
 
                // ドラッグの開始位置
                var dragStartX: Number = 0.0;
                var dragStartY: Number = 0.0;
 
                // マウスボタンが押されたら、サウンドを再生し、
                // 位置を保存する
                onMousePressed: function(event: MouseEvent) {
                    player.play();
                    dragStartX = locationX;
                    dragStartY = locationY;
                }
 
                // マウスボタンから離れたら、サウンドの再生を停止
                onMouseReleased: function(event: MouseEvent) {
                    player.stop();
                }
 
                // ドラッグされたら、位置を変化させる
                // 位置の変更に応じて、サウンドのバランス、ボリュームが変化
                onMouseDragged: function(event: MouseEvent) {
                    // x 座標の変更
                    // ウィンドウからはみ出た場合も考慮する
                    locationX = event.dragX + dragStartX;
                    if (locationX >= 200) {
                        locationX = 200;
                    } else if (locationX <= 0) {
                        locationX = 0;
                    }
 
                    // y 座標の変更
                    // ウィンドウからはみ出た場合も考慮する
                    locationY = event.dragY + dragStartY;
                    if (locationY >= 200) {
                        locationY = 200;
                    } else if (locationY <= 0) {
                        locationY = 0;
                    }
                }
            }
        ]
    }
}

javafx.scene.Groupクラスは座標を表すxアトリビュートやyアトリビュートがないため、青字で示したように、translateXアトリビュートとtranslateYアトリビュートにlocationX変数、locationY変数をバインドさせています。translateXアトリビュートとtranslateYアトリビュートを0とした場合、音符の楕円の中心が座標(0, 0)となるようにしてあります。

マウスボタンが押された時にコールされるonMousePressed関数でMediaPlayerオブジェクトのplay関数をコールし、再生を開始します。またこの時、ドラッグの開始位置をdragStartX変数とdragStartY変数に保持させておきます。

そして、マウスボタンから離れた時にコールされるonMouseReleased関数でMediaPlayerオブジェクトのstop関数をコールし、再生を停止します。

ドラッグによって描画位置を変更するのがonMouseDragged関数です。

オレンジで示しているように、ドラッグの開始位置とドラッグ量を足し合わせて、ノードの描画位置を決めています。オレンジの下のif文はSceneオブジェクトの領域からはみ出てしまった場合に、領域内に描画させるようにするために使用しています。

図1にサンプルの実行結果を示しました。今回も実行結果の図からアプレットページに飛びますので、ぜひ実際に試してみてください。また、省略した部分を含むスクリプトのソースも示しましたので、参考になさってください。

図1 ドラッグによりサウンドをコントロールする

図1 ドラッグによりサウンドをコントロールする

ムービーを扱う

次にムービーを再生してみましょう。ムービーを再生するには、javafx.scene.media.MediaViewクラスを使用します。

MediaViewクラスはmediaPlayerアトリビュートを持つので、ここにサウンドの場合と同様にMediaPlayerオブジェクトを生成して代入します。ムービーであってもMediaクラスが扱えるプロトコルはhttp:とfile:です。ただし、ムービーの場合、JARファイルにムービーファイルを含めることはできません。

C:\temp\movie.flvファイルを再生する場合、次のように記述します。

リスト4
// ムービーのメディア
var media = Media {
    source: "file:/C:/temp/movie.flv"
};
 
// ムービーのメディアを含んだメディアプレイヤ
var mediaPlayer = MediaPlayer {
    media: media
};

Stage {
    title: "Video Player"
    scene: Scene {
        width: media.width
        height: media.height
 
        // メディアプレイヤを表示するノード
        content: MediaView {
            mediaPlayer: mediaPlayer
        }
    }
}
 
// ムービーの再生
mediaPlayer.play();

ムービーのURIのプロトコルにfile:を使用する場合、file:///C:/temp/movie.flvとfile:の後に3つのスラッシュを表記すると、MediaUnsupportedException例外が発生するというバグがあります。URIとしてはfile:///が本来の仕様ですが、このバグを回避するため上記のようにfile:/C:/temp/movie.fllvという表記にしてあります。

ムービーの幅と高さはMediaオブジェクトから取得できるので、赤字で示しているようにSceneオブジェクトのサイズをムービーと同じにしています。

赤字で示した部分がMediaViewオブジェクトの生成部分です。ムービーの再生はサウンドの場合と同じく、MediaPlayerオブジェクトのplay関数をコールします。

MediaViewオブジェクトは変形させることも可能です。ただし、回転をさせる場合はrotatableアトリビュートをtrueに設定し、傾ける場合はtransformableアトリビュートをtrueにしておく必要があります。移動は特に指定する必要はありません。

ここでは、移動しながらムービーを再生するサンプルを作成しました。以下に垂直方向のアニメーションを行う部分を示します。

リスト5
// メディアプレイヤを表示するノード
var mediaView = MediaView {
    mediaPlayer: mediaPlayer
}
 
Stage {
    title: "Animated Movie Player"
    scene: Scene {
        width: 400
        height: 200
        fill: Color.BLACK
        content: mediaView
    }
}
 
// ムービーの再生
mediaPlayer.play();
 
// 垂直方向のアニメーション
Timeline {
    autoReverse: true
    repeatCount: Timeline.INDEFINITE
    keyFrames: [
        KeyFrame {
            time: 0s
            values: mediaView.y => 0
        },
        KeyFrame {
            time: 10s
            // ムービーの高さから移動範囲を決める
            values: mediaView.y => 200 - media.height
        }
    ]
}.play();

ムービーということを意識せずに、通常のノードと同様にアニメーションを行っています。

図2に実行結果を示します。また、アプレットページには省略した水平方向のアニメーションのスクリプトを含めたソースを示しました。

図2 移動しながらムービーの再生を行う

図2 移動しながらムービーの再生を行う

MediaViewクラスはムービー再生の最低限の機能しかありません。もし、再生ボタンやボリューム調整などが必要な場合には、JavaFXのサンプルSimpleVideoPlayerが提供しているMediaComponentクラスが使用できます。ぜひ参考にしてください。

JavaFX Production Suite

JavaFX Production Suiteは複数のツールから構成されています。表2にProduction Suiteに含まれるツールをまとめました。

表2 JavaFX Production Suiteに含まれるツール
ツール 説明
Adobe Illustrator用JavaFXプラグイン Illustratorで作成したアートワークをJavaFXフォーマットで保存するプラグイン
Adobe Photoshop用JavaFXプラグイン Photoshopで作成したアートワークをJavaFXフォーマットで保存するプラグイン
SVGコンバータ SVGファイルをJavaFXフォーマットに変換するツール
fx viewer JavaFXフォーマットのグラフィックファイルのビューア

JavaFXフォーマットについては後述します。

Production Suiteのインストール

では、Production Suiteをインストールしてみましょう。Production SuiteはJavaFX SDKと同じくhttp://javafx.com/もしくは、http://java.sun.com/javafx/downloads/からダウンロードできます。Windowsの場合、ダウンロードするファイルはjavafx_production_suite-1_0-windows-i586.exeです。

このファイルを実行すると、図3に示したようなウィザードが表示されます。

図3 JavaFX Production Suiteセットアップウィザード
図3 JavaFX Production Suiteセットアップウィザード

[Next >]をクリックして先に進むと、図4に示したライセンス条項が提示されます。ライセンスを確認後、[I accept the terms in the Licence Agreement]にチェックをし、[Next >]をクリックします。

図4 JavaFX Production Suiteのライセンス
図4 JavaFX Production Suiteのライセンス

次にProduction Suiteをインストールする場所を設定します図5⁠。[Next >]をクリックすると、インストールを開始します。

図5 インストール場所の設定
図5 Jインストール場所の設定

Adobe IllustratorおよびPhotoshop用JavaFXプラグインのインストール

IllustratorのプラグインはProduction Suiteをインストールしたディレクトリの直下にあるJavaFX Plugin for Adobe Illustratorディレクトリにあります。同様にPhotoshopのプラグインはJavaFX Plugin for Adobe Photoshopディレクトリです。

ここでは、Illustratorプラグインの場合を説明しますが、Photoshopでも同様に行うことができます。

JavaFX Plugin for Adobe Illustratorディレクトリの下にPlug-insディレクトリがあります。このディレクトリごとIllustratorをインストールしたディレクトリにコピーします。

筆者はAdobe CS4を使用しており、Illustrator CS4はC:\Program Files\Adobe\Adobe Illustrator CS4ディレクトリにインストールされています。ここにPlug-insディレクトリをコピーしました。コピーすると、⁠フラグイン」フォルダと「Plug-ins」フォルダを統合するかを確認するダイアログが表示されます。もちろん、統合してしまってかまわないので、⁠はい」をクリックします。また、同様に「Illustrator 形式 - 標準」フォルダと「Illustrator Formats」フォルダを統合するか確認するダイアログも表示されるので、⁠はい」をクリックします[2]⁠。

コピーした後、Illustratorを起動すると、図6のようにメニューバーの[File]の中に[Save for JavaFX...]が追加されているはずです。Photoshopの場合は、[ファイル]-[自動処理]-[Save for JavaFX...]になります。

図6 インストール場所の設定
図6 インストール場所の設定

JavaFXプラグインを使ってみる

では、実際にJavaFXプラグインを使用してみましょう。まずIllustratorで次のような絵を描いてみました。

図7 元となる図
図7 元となる図

JavaFXで扱えるようにするには、図6に示したようにメニューバーの[ファイル]から[Save for JavaFX...]を選択します。すると図8のようなダイアログが表示されます。

図8 JavaFXエクスポートダイアログ
図8 JavaFXエクスポートダイアログ

左側のペインに表示されているように、[Show Preview]をチェックするとプレビューが表示されます図9⁠。

図9 アートワークのプレビュー
図9 アートワークのプレビュー

では、ここで[Save]をクリックして、保存してみましょう。保存するとファイルの拡張子はfxzになります。

fxzファイルはどのような記述がなされているのでしょう。実をいうと、fxzファイルはZIP形式で圧縮されたファイルです。つまり、そのまま展開することができます。ここではartwork.fxzというファイルにしたので、それを展開してみました。

展開するとcontent.fxdファイルが含まれていることがわかります。次にcontent.fxdファイルを示します(見やすさのため整形してあります⁠⁠。

リスト6
/*
 * Generated by JavaFX plugin for Adobe Illustrator.
 * Created on Tue Jan 17 14:56:50 JST 2009
 * Build date: 2008-11-26_00-26-18
 * Build id: 1.22768768E9
 * Revision: 2210.0
 */
//@version 1.0
 
Group {
    content: [
        Group {
            content: [
                Rectangle {
                    fill: Color.rgb(0xff,0x33,0x33,1.0)
                    stroke: Color.BLACK
                    strokeWidth: 1.0
                    x: 0.5
                    y: 0.5
                    width: 120.0
                    height: 80.0
                },
                SVGPath {
                    fill: Color.rgb(0x66,0x99,0xff,1.0)
                    stroke: Color.BLACK
                    strokeWidth: 1.0
                    content: "M70.05,84.32 C70.05,56.71 92.43,
                        34.32 120.05,34.32 C147.66,34.32 170.05,
                        56.71 170.05,84.32 C170.05,111.94 147.66,
                        134.32 120.05,134.32 C92.43,134.32 70.05,
                        111.94 70.05,84.32 Z "
                },
            ]
        },
    ]
}

なんのことはない、ごくごく普通のJavaFX Scriptのスクリプトでした。このFXDファイルの記述がJavaFXフォーマットと呼ばれているものです。

では、このJavaFXフォーマットのファイルをスクリプトの中から扱ってみましょう。ここではpluginsample1というNetBeansのプロジェクトを作成しました。そして、JavaFXフォーマットファイルをロードするためのライブラリをインポートします。Production Suiteをインストールしたディレクトリの直下のLibrariesディレクトリにあるjavafx-fxd-1.0.jarが、そのライブラリです。

ここでは、pluginsample1ディレクトリの下にlibディレクトリを作成し、そこにjavafx-fxd-1.0.jarファイルをコピーしました。次にNetBeansで、pluginsample1プロジェクトのLibrariesを右クリックします。すると、図10のようにポップアップメニューが表示されるので、[Add Jar/Folder]を選択します。ファイルチューザが表示されるので、先ほどコピーしたjavafx-fxd-1.0.jarファイルを指定します。

図10 JARファイルの追加
図10 JARファイルの追加

JARファイルが追加されると、図11のようにLibrariesの下にjavafx-fxd-1.0.jarが表示されます。

図11 javafx-fxd-1.0.jarファイルの追加結果
図11 javafx-fxd-1.0.jarファイルの追加結果

次に、先ほど作成したartsample.fxzをパッケージに含めておきましょう。ここでは、pluginsample1プロジェクトのsrcディレクトリにコピーしました。

スクリプトからJavaFXフォーマットファイルをロードするには、javafx-fxd-1.0.jarに含まれているjavafx.fxd.FXDLoaderクラスを使用します。以下にFXZファイルの内容を描画するスクリプトを示します。

リスト7
Stage {
    title: "Plugin Sample"
    scene: Scene {
        width: 200
        height: 200
        content: {
            // FXZファイルのロード
            FXDLoader.load("{__DIR__}artwork.fxz");
} } }

JavaFXフォーマットファイルのロードはFXDLoaderクラスのload関数で行います。引数はURIで、戻り値がJavaFXフォーマットで表された描画ノードになります。

このスクリプトを実行した結果を図12に示します。

図12 JavaFXフォーマットファイルの描画
図12 JavaFXフォーマットファイルの描画

IllustratorやPhotoshopで作成したアートワークを1つのものとして扱うのであれば、この方法でかまいません。しかし、たとえば図12の四角と丸を別々に扱いたい場合もあるはずです。

このような場合、IllustratorやPhotoshopで別々に扱う描画オブジェクトに名前をつけておきます。名前をつける場合、レイヤーとオブジェクトのどちらでもかまいませんが、名前にjfx:という接頭語をつけておきます。こうすることで、JavaFXがjfx:の接頭語のついたレイヤーもしくはオブジェクトを個別に扱うことができます。

図13にIllustratorの例を示しました。図13では四角をjfx:rectangleレイヤー、丸をcircleレイヤーにあるjfx:circleオブジェクトと名前をつけました。

図13 Illustratorでのレイヤー分割
図13 Illustratorでのレイヤー分割

そして、再びJavaFXプラグインを使用して、FXZファイルを生成します。名前付けしたオブジェクトを個々に取得するにはFXDLoaderクラスのloadContent関数を使用します。loadContent関数の戻り値はjavafx.fxd.FXDContentクラスです。

FXDContentクラスはgetShape関数とgetNode関数を持ちます。前者がIllustratorでオブジェクトに名前付けしたもの、後者がレイヤーに名前付けしたものを取得できます。2つの関数とも、引数にはjfx:を除いた名前を用います。

リスト8
// FXZファイルのロード
var content: FXDContent = FXDLoader.loadContent("{__DIR__}artwork2.fxz");

// FXDContentオブジェクトから描画オブジェクトを取得
// レイヤーに名前付けしたものはNodeクラス、
// オブジェクトに名前付けしたものはShapeクラスで表される
var circle: Shape = content.getShape("circle");
var square: Node = content.getNode("rectangle");
 
Stage {
    title: "Plugin Sample"
    scene: Scene {
        width: 200
        height: 150
        content: [circle, square]
    }
}

ここでは、丸がShapeオブジェクト、四角がNodeオブジェクトとして扱われます。別々に取得できたので、描画順序を変えてみたの結果が図14です。

図14 描画オブジェクトを個別に扱う
図14 描画オブジェクトを個別に扱う

今までのサンプルは、FXDLoaderクラスとFXDContentクラスを直接使用しましたが、この2つのクラスの機能をまとめたjavafx.fxd.UiStubクラスも使用することができます。興味がある方はProduction Suiteをインストールしたディレクトリ下のLibrariesディレクトリにAPIドキュメントがあるので、そちらをご参照ください。

最後にもう1つサンプルを紹介しましょう。桜の花びらが1枚落ちていくというアニメーションを行うスクリプトです。

Illustratorで作成したアートワークを図15に示します。黒い線は桜の花びらが落ちる軌跡を示しています。つまり、前回使用したjavafx.animation.transition.PathTransitionクラスで使用するパスをIllustratorで作成してしまったわけです。

このような複雑な曲線をプログラムだけで作成するのは大変ですが、Illustratorであれば簡単に作成できます。このような曲線はリスト6にも示したようにjavafx.scene.shape.SVGPathクラスで表されます。PathTransitionクラスで使用するガイドパスを作成するjavafx.animation.transition.AnimationPathクラスには、SVGPathオブジェクトを引数にとるcreateFromPath関数が定義されています。この関数を使うことで、Illustratorで作成した曲線に沿ってアニメーションすることが可能になります。

図15 桜と花びらの落ちる軌跡
図15 桜と花びらの落ちる軌跡

ここではスクリプトの解説は行いませんが、興味がある方は図16からリンクしているアプレットのページにスクリプトを示しておきましたので、参考にしてください。

図16 桜の花びらが落ちるアニメーション

図16 桜の花びらが落ちるアニメーション

おすすめ記事

記事・ニュース一覧