Spidermonkey Panelでアルバムアートを表示するスクリプトの解説です。久しぶりにfoobar2000を触ろうと思ったら、以前自分が書いたコードですら何をしてるのかわからないほど忘却していたので、自分が思い出すのも兼ねて。
Spidermonkey Panelとは
Spidermonkey Panelは、JavaScriptを使ってfoobar2000を操作するコンポーネントです。JavaScriptに精通していないと使えない気がしますが、実はそんなことはなく、ほんの基礎を学ぶだけでもある程度のことはできます。なぜかというと、JavaScriptはブラウザ上で動作しWEBページを操作する言語ですが、Spidermonkey PanelではWEBページを操作するJavaScriptの機能は使われないからです。イメージとしては、JavaScriptの文法だけを使っている感じでしょうか。
コード
Spidermonkey Panelのサンプルを少しアレンジしただけの短いコードです。使用している画像はこちらからダウンロードできます。foobar2000.exeがあるディレクトリにimagesというフォルダを作り、その中にこの画像を配置すると正しく動作します。
window.DefinePanel('ScriptName', {author: 'Someone' ,version: '1.0'});
const defaultPath = `${fb.FoobarPath}\images\\default.png`;
let art, ww, wh;
if (fb.IsPlaying || fb.IsPaused) {
art = utils.GetAlbumArtV2(fb.GetNowPlaying());
} else {
art = gdi.Image(defaultPath);
}
function on_playback_stop(reason) {
art = gdi.Image(defaultPath);
window.Repaint();
}
function on_playback_new_track(handle) {
art = utils.GetAlbumArtV2(handle);
window.Repaint();
}
function on_size(width, height) {
ww = width;
wh = height;
}
function on_paint(gr) {
const sw = ww / art.Width;
const sh = wh / art.Height;
const scale = Math.min(sw, sh);
let dstX = 0;
let dstY = 0;
if (sw < sh) {
dstY = (wh - art.Height * scale) / 2;
} else if (sw > sh) {
dstX = (ww - art.Width * scale) / 2;
}
gr.DrawImage(art, dstX, dstY, art.Width*scale, art.Height*scale, 0, 0, art.Width, art.Height);
if(fb.IsPlaying || fb.IsPaused) {
gr.DrawRect(0, 0, ww - 1, wh - 1, 1, 0xFF808080);
}
}
解説
window.DefinePanel('ScriptName', {author: 'Someone' ,version: '1.0'});
この文は、スクリプトの名前・作者・バージョン、そしてドラッグアンドドロップ機能を有効にするかを定義する文で、Spidermonkey Panelの関数です。今回は使用しないのでドラッグアンドドロップ機能は省略しています。
const defaultPath = `${fb.FoobarPath}\images\\default.png`;
let art, ww, wh;
上の行は定数defaultPathを定義し、同時に画像のパスを入れています。fb.FoobarPathはfoobar2000.exeが存在するディレクトリのパスを返す、Spidermonkey Panelの関数です。fb.FoobarPathが返すパスの後ろには\が含まれます。下の行は空の変数art、ww、whの3つを定義しています。定数defaultPathに入れているのは再生されていないときに表示する画像のパスで、artは後々アルバムアートを入れるために定義した変数です。
if(fb.IsPlaying || fb.IsPaused) {処理} else {処理}
JavaScriptの条件分岐です。まず、fb.IsPlayingは再生中であるか否か、fb.IsPausedは一時停止中であるか否かを返すSpidermonkey Panelの関数です。それぞれtrueかfalseを返します。間にある||は「▲▲または△△」という意味の、JavaScriptの論理演算子の一つです。なので「foobar2000が再生中または一時停止中なら」中の処理を行い、条件に合わなかったら、つまりfoobar2000が停止中の場合はelseの中の処理が行われます。
art = utils.GetAlbumArtV2(fb.GetNowPlaying());
再生中または一時停止中に行う処理です。関数に関数を渡しているので少しわかりにくいかも知れません。
utils.GetAlbumArtV2は、渡されたファイル情報を元にアルバムアートを取得して返すSpidermonkey Panelの関数です。これに渡されているfb.GetNowPlayingは、再生中のファイル情報を返すSpidermonkey Panelの関数で、こちらは値を渡す必要はありません。なので、正しくはfb.GetNowPlaying関数を実行して返ってきた値を、utils.GetAlbumArtV2関数に渡しています。
また、()は括弧演算子というJavaScriptの演算子で、関数名の後ろに付けるとその関数を実行します。utils.GetAlbumArtV2の後ろにあるのも括弧演算子です。
art = gdi.Image(defaultPath);
今度は停止中の処理です。
gdi.Image関数は、画像のパスを渡されるとその画像を取得して返す、Spidermonkey Panelの関数です。ちょっとクドいですが、gdi.Image関数の後ろに括弧演算子が付いています。今回は括弧の中に定数が入っており、それを元に関数が実行されます。
function on_playback_stop(reason) {処理}
この関数は、再生または一時停止している曲が停止したとき、Spidermonkey Panelが呼び出すコールバック関数です。難しく考えず、曲が停止したときに何か行いたいときは、この関数の中に処理を記述すると覚えておくと良いでしょう。
関数名の後ろに括弧演算子が付いていると実行されると書きましたが、Spidermonkey Panelのコールバック関数は、定義されているだけでもfoobar2000が特定の状態になったら自動的に実行されます。この点が他の関数とは違うので覚えておきましょう。
art = gdi.Image(defaultPath); window.Repaint();
gdi.Image関数は先ほども登場しましたが、画像のパスを渡されるとその画像を取りに行って返す、Spidermonkey Panelの関数でした。つまり、曲が停止したらデフォルト画像に切り替えることを想定した処理になります。
window.Repaintは、パネルの表示を再描画するSpidermonkey Panelの関数です。この文がないと、変数に入っている画像が変わっても表示上の画像はすぐに変わりません。
なぜまたさっきと同じ処理を行うのか?と疑問に感じるかも知れません。まず、JavaScriptは上から下へ順番に実行されます。なので、先ほどの処理が先に行われるわけですが、それはこのスクリプトが有効になったときに1度だけです。対してon_playback_stop関数の中の処理は、foobar2000が停止される度に行われます。これからいくつか登場するコールバック関数も同じで、foobar2000が特定の状態になったときや、特定の操作が行われる度に実行されます。
function on_playback_new_track(handle) {処理}
これもSpidermonkey Panelのコールバック関数で、新しい曲の再生が開始したときに呼ばれます。コールバック関数は、それぞれ呼ばれたら何らかのデータを一緒に持ってきます。そのデータが括弧内のhandleです。これは、このデータを持っている関数内でのみ使用できます。
art = utils.GetAlbumArtV2(handle); window.Repaint();
utils.GetAlbumArtV2は、渡されたファイル情報を元にアルバムアートを取得して返すSpidermonkey Panelの関数でした。今度は先に説明したhandleを渡しています。console.log(handle)としてみるとわかりますが、handleの中には再生中のファイルのパスなどが入っています。これは、先のfb.GetNowPlaying関数を実行したときに返ってくるデータと同じです。表示をすぐに切り替えるために、window.Repaintを実行します。
function on_size(width, height) {処理}
ウィンドウを最大化したときなど、パネルのサイズが変更されたときに呼ばれるSpidermonkey Panelのコールバック関数です。widthとheightにはパネルの幅と高さが入っています。
ww = width; wh = height;
最初に定義したwwとwhにパネルの幅と高さを入れています。パネル上に何かを表示する場合、ほぼ必須となる処理です。この処理を入れておけば、ウィンドウサイズが変わったときに変数に入っている幅と高さが更新されるので、表示位置を対応させることができます。また、画像をスケーリングする際にもこれらの数値が必要になります。
function on_paint(gr) {処理}
パネルが描画可能な状態になったら呼び出される、Spidermonkey Panelのコールバック関数です。画像や文字列など、何かパネル上に表示したいときはこの関数の中に処理を書く、と覚えておきましょう。
const sw = ww / art.Width;
const sh = wh / art.Height;
const scale = Math.min(sw, sh);
let dstX = 0;
let dstY = 0;
if (sw < sh) {
dstY = (wh - art.Height * scale) / 2;
} else if (sw > sh) {
dstX = (ww - art.Width * scale) / 2;
}
長いのでわかりにくいと思いますが、パネル中央にアスペクト比を維持して画像を表示する時に必要な、座標や拡大/縮小率を計算する処理になります。この一連の処理はよく使われます。
1・2行目ですが、画像が入っている変数・定数(または画像を返す関数など)に.Width・.Heightを付けることで、その画像の幅や高さを返してくれます。これもSpidermonkey Panelの関数です。続いてMath.minはJavaScriptの関数で、渡された数値を比較して最も小さいものを返します。
200x200のパネルに1000x1000の画像を表示する場合を考えてみます。幅も高さも同じ長さなのでsw・shどちらも200÷1000=0.2となり、scaleの値も0.2になります。条件分岐の処理はswとshの値が違う場合に行われるのでスルーされ、よってdstY・dstXともに0となります。
gr.DrawImage(art, dstX, dstY, art.Width*scale, art.Height*scale, 0, 0, art.Width, art.Height);
DrawImageはパネル上に画像を表示するSpidermonkey Panelの関数です。コンマで区切られた9つのデータを渡していますが、左から「画像」「画像を表示するパネル上のX点(ピクセル)」「同じくY点」「XYが交わる点を基点とした幅」「同じく高さ」「表示する画像のX点」「同じくY点」「XYが交わる点を基点とした画像の幅」「同じく高さ」となっています。なんかややこしいですが、前半は画像をパネルのどこに配置するのか、後半は画像のどの部分を表示するのか(画像の切り抜き)を指定しています。
もう一つ重要なポイントとして、DrawImageの前にgr.が付いています。このgrはon_paint関数が持っているデータですが、単なる文字列ではなく複数の関数が入った関数(正しくは複数の関数をメソッドとして持つオブジェクト)が入っています。その複数の関数の一つがDrawImageで、その他の関数についてはdocのGdiGraphicsの項に全て載っています。先に書いたとおり、コールバック関数が持つデータはその関数内でしか使用できないので、gr.関数もまたon_paint内でしか使用できないということを覚えておいてください。
if(fb.IsPlaying || fb.IsPaused) {処理}
同じ文が先に出ていますが、今度はelseの処理がありません。この場合は、条件に合えば中の処理が行われ、合わなかったら何も行われず次の文の処理に移ります。
gr.DrawRect(0, 0, ww-1, wh-1, 1, 0xFF808080);
中を塗りつぶさない枠だけの矩形を描画するSpidermonkey Panelの関数です。渡すデータは左から「矩形を描画するパネル上のX点(ピクセル)」「同じくY点」「XYが交わる点を基点とした矩形の幅(線の太さを含む)」「同じく高さ」「線の太さ」「色」です。これはアルバムアートの外周にグレーの枠を付けているのですが、トップ画像では馴染んでしまっていてわかりにくいです。それとこの場合、幅と高さから線の太さを引かなければいけないので、線の太さは定数にした方が良さそうです。
おわりに
少々冗長だったでしょうか。時間があれば、クリックイベントやメニューの作り方、あとは文字列の表示などの説明もやろうかなと思っています。
0 件のコメント:
コメントを投稿